Skip to main content
RapidDev - Software Development Agency
supabase-tutorial

How to Debug Supabase Edge Functions

To debug Supabase Edge Functions, use supabase functions serve for local development with hot reload and console.log output in your terminal. For deployed functions, check logs in the Dashboard under Edge Functions then Logs, or use supabase functions logs from the CLI. Common errors include BOOT_ERROR from bad imports, TIME_LIMIT from exceeding the 150-second wall-clock timeout, and missing CORS headers causing silent failures in the browser.

What you'll learn

  • How to serve Edge Functions locally with hot reload for rapid debugging
  • How to read and filter Edge Function logs in the Dashboard and CLI
  • How to diagnose common errors like BOOT_ERROR, TIME_LIMIT, and import failures
  • How to add proper error handling and structured logging to Edge Functions
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Advanced8 min read15-20 minSupabase (all plans), Supabase CLI v1.50+, Deno runtimeMarch 2026RapidDev Engineering Team
TL;DR

To debug Supabase Edge Functions, use supabase functions serve for local development with hot reload and console.log output in your terminal. For deployed functions, check logs in the Dashboard under Edge Functions then Logs, or use supabase functions logs from the CLI. Common errors include BOOT_ERROR from bad imports, TIME_LIMIT from exceeding the 150-second wall-clock timeout, and missing CORS headers causing silent failures in the browser.

Debugging Supabase Edge Functions Locally and in Production

Supabase Edge Functions run on a Deno-compatible runtime at the edge. When things go wrong, errors can be opaque — a function might return a 500 error with no visible output, or fail to boot entirely. This tutorial covers the complete debugging workflow: local development with supabase functions serve, reading production logs, diagnosing the most common errors (BOOT_ERROR, TIME_LIMIT, import failures), and adding structured error handling to your functions.

Prerequisites

  • Supabase CLI installed and linked to your project
  • At least one Edge Function created (supabase functions new)
  • Basic knowledge of TypeScript and the Deno runtime
  • Docker installed (required for supabase functions serve)

Step-by-step guide

1

Serve functions locally with hot reload

The fastest debugging workflow is local development. Run supabase functions serve to start a local Edge Function server with hot reload. Every time you save a file, the function restarts automatically. Console output (console.log, console.error) appears directly in your terminal. You can test functions with curl or your application pointing to the local URL.

typescript
1# Start local Edge Function server
2supabase functions serve
3
4# The function is available at:
5# http://localhost:54321/functions/v1/your-function-name
6
7# Test with curl
8curl -i --location --request POST \
9 'http://localhost:54321/functions/v1/hello-world' \
10 --header 'Authorization: Bearer YOUR_ANON_KEY' \
11 --header 'Content-Type: application/json' \
12 --data '{"name": "test"}'

Expected result: The function server starts and logs appear in real time as you make requests.

2

Add structured error handling to your function

Edge Functions should catch all errors and return meaningful responses. Without error handling, an unhandled exception returns a generic 500 error with no body, making debugging impossible. Wrap your function logic in a try/catch block, log the full error object, and return a structured JSON error response with an appropriate status code.

typescript
1// supabase/functions/process-data/index.ts
2import { corsHeaders } from '../_shared/cors.ts'
3
4Deno.serve(async (req) => {
5 // Handle CORS preflight
6 if (req.method === 'OPTIONS') {
7 return new Response('ok', { headers: corsHeaders })
8 }
9
10 try {
11 const body = await req.json()
12 console.log('Request body:', JSON.stringify(body))
13
14 if (!body.id) {
15 return new Response(
16 JSON.stringify({ error: 'Missing required field: id' }),
17 { status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
18 )
19 }
20
21 // Your logic here...
22 const result = { success: true, id: body.id }
23 console.log('Response:', JSON.stringify(result))
24
25 return new Response(
26 JSON.stringify(result),
27 { status: 200, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
28 )
29 } catch (error) {
30 console.error('Function error:', error.message, error.stack)
31 return new Response(
32 JSON.stringify({ error: 'Internal server error', details: error.message }),
33 { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
34 )
35 }
36})

Expected result: Errors return structured JSON responses with details, and full stack traces appear in the logs.

3

Read production logs in the Dashboard and CLI

For deployed functions, access logs in two ways. In the Dashboard, go to Edge Functions in the left sidebar, click on your function, then click the Logs tab. You can filter by time range and search for specific text. From the CLI, use supabase functions logs to stream logs in real time. All console.log and console.error output from your function appears in these logs, along with system messages for boot errors and timeouts.

typescript
1# Stream logs for a specific function
2supabase functions logs hello-world
3
4# Stream logs for all functions
5supabase functions logs
6
7# View logs from the Dashboard:
8# Dashboard Edge Functions [function name] Logs tab

Expected result: You can see all function invocations, their console output, and any system errors.

4

Diagnose BOOT_ERROR and import failures

BOOT_ERROR means the function failed to start. This is almost always caused by an import that could not be resolved. Common causes: importing a Node.js module that is not available in Deno, a typo in the import path, or using bare specifiers without the npm: prefix. Edge Functions use the Deno runtime, so Node.js-specific APIs like fs, path, or process are not available. Use npm: prefix for npm packages and Deno-compatible alternatives for Node.js APIs.

typescript
1// WRONG: bare import (causes BOOT_ERROR)
2import express from 'express'
3
4// CORRECT: use npm: prefix for npm packages
5import Stripe from 'npm:stripe@14'
6import { createClient } from 'npm:@supabase/supabase-js@2'
7
8// WRONG: Node.js API (not available in Deno)
9import fs from 'fs'
10import path from 'path'
11
12// CORRECT: Deno equivalent
13const file = await Deno.readTextFile('./data.json')
14
15// WRONG: relative import without .ts extension
16import { helper } from '../_shared/utils'
17
18// CORRECT: include the .ts extension
19import { helper } from '../_shared/utils.ts'

Expected result: Import errors are identified and fixed, and the function boots successfully.

5

Debug TIME_LIMIT and performance issues

Edge Functions have a 150-second wall-clock timeout for the initial response and 2 seconds of CPU time per request. TIME_LIMIT errors mean your function exceeded one of these limits. Common causes: making slow external API calls without timeouts, processing large datasets synchronously, or infinite loops. Add timeouts to all external requests and break large tasks into smaller chunks.

typescript
1// Add timeout to fetch requests
2const controller = new AbortController()
3const timeoutId = setTimeout(() => controller.abort(), 10000) // 10 second timeout
4
5try {
6 const response = await fetch('https://api.example.com/data', {
7 signal: controller.signal,
8 headers: { 'Authorization': 'Bearer ...' },
9 })
10 clearTimeout(timeoutId)
11
12 if (!response.ok) {
13 console.error(`API returned ${response.status}: ${await response.text()}`)
14 throw new Error(`API error: ${response.status}`)
15 }
16
17 const data = await response.json()
18 console.log(`API returned ${data.length} items`)
19} catch (error) {
20 if (error.name === 'AbortError') {
21 console.error('Request timed out after 10 seconds')
22 }
23 throw error
24}

Expected result: External API calls have explicit timeouts and you can identify which calls are causing the function to exceed time limits.

Complete working example

supabase/functions/debug-example/index.ts
1// supabase/functions/debug-example/index.ts
2// Edge Function with comprehensive error handling and logging
3
4import { createClient } from 'npm:@supabase/supabase-js@2'
5import { corsHeaders } from '../_shared/cors.ts'
6
7Deno.serve(async (req) => {
8 if (req.method === 'OPTIONS') {
9 return new Response('ok', { headers: corsHeaders })
10 }
11
12 const requestId = crypto.randomUUID()
13 const startTime = Date.now()
14 console.log(`[${requestId}] Function invoked: ${req.method} ${req.url}`)
15
16 try {
17 // Parse request body
18 const body = await req.json().catch(() => null)
19 if (!body) {
20 return new Response(
21 JSON.stringify({ error: 'Invalid JSON body' }),
22 { status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
23 )
24 }
25 console.log(`[${requestId}] Body:`, JSON.stringify(body))
26
27 // Initialize Supabase client
28 const supabase = createClient(
29 Deno.env.get('SUPABASE_URL')!,
30 Deno.env.get('SUPABASE_ANON_KEY')!,
31 {
32 global: {
33 headers: { Authorization: req.headers.get('Authorization')! },
34 },
35 }
36 )
37
38 // Verify auth
39 const { data: { user }, error: authError } = await supabase.auth.getUser()
40 if (authError || !user) {
41 console.error(`[${requestId}] Auth error:`, authError?.message)
42 return new Response(
43 JSON.stringify({ error: 'Unauthorized' }),
44 { status: 401, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
45 )
46 }
47 console.log(`[${requestId}] Authenticated user: ${user.id}`)
48
49 // Fetch data with timeout
50 const { data, error: dbError } = await supabase
51 .from('todos')
52 .select('*')
53 .eq('user_id', user.id)
54
55 if (dbError) {
56 console.error(`[${requestId}] DB error:`, dbError.message)
57 throw dbError
58 }
59
60 const elapsed = Date.now() - startTime
61 console.log(`[${requestId}] Success. ${data.length} rows. ${elapsed}ms`)
62
63 return new Response(
64 JSON.stringify({ data, requestId }),
65 { status: 200, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
66 )
67 } catch (error) {
68 const elapsed = Date.now() - startTime
69 console.error(`[${requestId}] Error after ${elapsed}ms:`, error.message)
70 return new Response(
71 JSON.stringify({ error: error.message, requestId }),
72 { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
73 )
74 }
75})

Common mistakes when debugging Supabase Edge Functions

Why it's a problem: Importing Node.js packages without the npm: prefix, causing BOOT_ERROR

How to avoid: Edge Functions use Deno, not Node.js. Prefix npm packages with npm: — for example, import Stripe from 'npm:stripe@14' instead of import Stripe from 'stripe'.

Why it's a problem: Not including CORS headers in error responses, causing the browser to show a CORS error instead of the actual error

How to avoid: Add CORS headers to every response, including 400, 401, and 500 error responses. Import the shared corsHeaders object and spread it into every Response.

Why it's a problem: Making external API calls without timeouts, leading to TIME_LIMIT errors

How to avoid: Use AbortController with setTimeout to add explicit timeouts to all fetch calls. A 10-second timeout is a good default for most API calls.

Best practices

  • Use supabase functions serve for local development with instant feedback and hot reload
  • Wrap all function logic in try/catch and return structured JSON error responses
  • Include CORS headers in every response — including error responses — to avoid masking real errors
  • Add request IDs and timestamps to log messages for easier debugging in production
  • Use the npm: prefix for all npm package imports in Edge Functions
  • Include .ts file extensions in all relative imports — Deno requires explicit extensions
  • Add explicit timeouts to all external HTTP requests using AbortController
  • Test functions locally with curl before deploying to catch errors early

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

My Supabase Edge Function returns a 500 error with no body. Show me how to add proper error handling, CORS headers, and structured logging so I can debug production issues. Also explain BOOT_ERROR and TIME_LIMIT errors.

Supabase Prompt

Create a Supabase Edge Function template with comprehensive error handling, request ID logging, CORS headers on all responses, auth verification, and a try/catch that returns structured JSON errors. Show me how to test it locally with supabase functions serve.

Frequently asked questions

What does BOOT_ERROR mean in Supabase Edge Functions?

BOOT_ERROR means the function failed to initialize before handling any requests. This is almost always caused by a bad import — either an npm package without the npm: prefix, a typo in the import path, or a missing .ts file extension on a relative import.

What is the time limit for Edge Functions?

Edge Functions have a 150-second wall-clock timeout for the initial response and 2 seconds of CPU time per request. On Pro plans, background tasks can run up to 400 seconds after the initial response is sent.

How do I access environment variables in Edge Functions?

Use Deno.env.get('VARIABLE_NAME'). Default secrets (SUPABASE_URL, SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY, SUPABASE_DB_URL) are automatically available. Set custom secrets with supabase secrets set KEY=VALUE.

Why does my function work locally but fail after deployment?

Common causes: environment variables not set with supabase secrets set, npm packages that work in local Deno but fail at the edge, or CORS issues when calling from a browser. Check the production logs in the Dashboard for the specific error.

Can I use console.log in deployed Edge Functions?

Yes, all console.log and console.error output appears in the Edge Function logs in the Dashboard and via supabase functions logs in the CLI.

How do I test an Edge Function that requires authentication?

Pass the Authorization header with a valid JWT when testing with curl: curl -H 'Authorization: Bearer YOUR_JWT_TOKEN'. For local testing, get a JWT by signing in via the Supabase client and copying the access_token from the session.

Can RapidDev help debug and optimize my Edge Functions?

Yes, RapidDev can diagnose Edge Function failures, add proper error handling and logging, optimize performance to avoid TIME_LIMIT errors, and set up monitoring for production deployments.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.