Edge Function 401 error response
Last edited: 4/3/2026
A 401 response from an Edge Function means either:
- The function failed the legacy auth verification check
- Your function's logic deliberately returned a 401 response
Quick triage#
Check the response body returned by the request
Case 1: "Invalid JWT" or "Missing authorization header"#
1{ "code": 401, "message": "Invalid JWT" }1{ "code": 401, "message": "Missing authorization header" }Both of these messages come from the legacy auth verification check
Go to: Built-in JWT check failures
Case 2: Custom message or empty body#
If the response body contains a message you coded, or nothing at all, then your function code did execute and returned a 401 itself.
Go to: Your function returned a 401
Case 3: Not sure#
Run this query in Log Explorer to classify recent 401s:
1select2 cast(timestamp as datetime) as timestamp,3 req.pathname as function_name,4 case5 when metadata.execution_id is not null then 'your_code_returned_401'6 when metadata.execution_id is null7 and (8 new_auth.prefix is not null9 or legacy_payload.algorithm != 'HS256'10 ) then 'incompatible_keys'11 when metadata.execution_id is null12 and (13 (legacy_auth_data.invalid is not null or new_auth.error is not null)14 or legacy_payload.algorithm = 'HS256'15 ) then 'invalid_key'16 when metadata.execution_id is null17 and legacy_auth_data is null18 and new_auth.prefix is null then 'missing_auth_header'19 end as cause20from21 function_edge_logs22 -- unnesting metadata23 cross join UNNEST(metadata) as metadata24 cross join UNNEST(metadata.request) as req25 cross join UNNEST(metadata.response) as res26 -- unnesting auth details27 left join UNNEST(req.sb) as sb28 left join UNNEST(sb.apikey) as apikey29 left join UNNEST(apikey.authorization) as new_auth30 left join UNNEST(sb.jwt) as legacy_jwt31 left join UNNEST(legacy_jwt.authorization) as legacy_auth_data32 left join UNNEST(legacy_auth_data.payload) as legacy_payload33where res.status_code = 40134order by timestamp desc35limit 50;Depending on the output, you can use this table to find the appropriate debugging section:
| Value | Go to |
|---|---|
your_code_returned_401 | Your function returned a 401 |
incompatible_keys | Incompatible key format |
invalid_key | Invalid key |
missing_auth_header | Missing Authorization header |
Your function returned a 401#
Your function ran, and somewhere in your code, its logic returned a 401.
Example:
1return new Response(JSON.stringify(data), {2 headers: { ...corsHeaders, 'Content-Type': 'application/json' },3 status: 401, // <-- you set this4})How to fix:
- Search your function code for
401. Look for explicit status codes onResponseobjects. - Trace the condition that triggered it. If you're interacting with a third-party API in your code, that service may be returning 401 that you're forwarding in the response object.
- Add logging before the return so future occurrences leave a trace:
1console.error('Returning 401 - reason:', reason)See: Error handling in Edge Functions
Built-in JWT check failures#
Supabase Edge Functions have a legacy auth verification check that runs before your code. When it fails, your function never executes, and you get a 401 with "Invalid JWT" or "Missing authorization header" directly from the platform.
Supabase now recommends turning off this built-in check and managing authentication directly in your function code, giving you more control over access. See Securing Edge Functions.
The subsections below cover specific failure modes.
Incompatible key format#
Your project uses the new asymmetric keys for authentication. However, the legacy auth verification check only understands the legacy format.
Fix: Disable the built-in JWT check using one of the below methods and optionally handle auth in your function code
Invalid key#
The built-in check is enabled and the key you sent doesn't match your project's keys.
Fix (recommended): Disable the built-in check using the steps in Incompatible key format.
Fix (alternative): If you want to keep the built-in check, ensure you're sending a valid key. Use one of your legacy API keys with the Supabase client library when making your request.
1const supabase = createClient('https://xyzcompany.supabase.co', 'anon-key-or-service_role-key')Missing authorization header#
The built-in check is enabled but your request has no Authorization header at all.
If you're using a Supabase client library, the header is added automatically. If you're calling the function from an external client (cURL, fetch, etc.), you need to supply it:
1curl -L -X POST 'https://PROJECT_REF.supabase.co/functions/v1/hello-world' \2 -H 'Authorization: Bearer YOUR_ANON_OR_SERVICE_ROLE_KEY' \3 --data '{"name":"Functions"}'Alternatively, you can disable the built-in check entirely (see Incompatible key format).
Additional resources#
- Securing Edge Functions with Auth
- Logging Edge Function Requests
- Error Handling Edge Functions
- Quickstart: Dashboard deployment
- Quickstart: CLI deployment
Still stuck?#
- Check the Discord, Supabase GitHub Discussions, and Reddit page for similar reports that can help with debugging
- Open a support ticket for your project if the problem persists and you believe it is a platform issue