Supabase Edge Functions error tracking your coding agent can read
Supabase Edge Functions run on Deno, and when one throws, the caller sees a 500 and the details sit in the function logs. That is where the server code of a Lovable or Bolt app built on Supabase runs too. One shared file reports each error to Runheed with fetch (no package to install), and your coding agent reads the error, the request and the stack trace over MCP.
Set it up
1.In your project (Supabase dashboard: Edge Functions > Secrets, or the CLI) add the pk_... part of your project DSN as RUNHEED_KEY.
terminalbashsupabase secrets set RUNHEED_KEY=pk_...2.Create one shared file. It sends the error to the HTTP ingest endpoint and never lets reporting break your function.
supabase/functions/_shared/runheed.tstypescriptconst KEY = Deno.env.get('RUNHEED_KEY'); const INGEST = 'https://runheed.com/api/ingest'; function frames(stack = '') { return stack.split('\n').slice(1).flatMap((line) => { const m = line.match(/at (?:(.+?) \()?(\S+?):(\d+):(\d+)\)?$/); if (!m) return []; return [{ function: m[1] ?? '<anonymous>', filename: m[2], lineno: Number(m[3]), colno: Number(m[4]), inApp: !m[2].startsWith('ext:') && !m[2].includes('deno.land') }]; }); } export async function reportError(err: unknown, req?: Request, extra: Record<string, unknown> = {}) { if (!KEY) return; const e = err instanceof Error ? err : new Error(String(err)); try { await fetch(INGEST, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Runheed-Key': KEY }, body: JSON.stringify({ errorType: e.name || 'Error', errorMessage: e.message || String(err), stacktrace: frames(e.stack), platform: 'deno', runtime: `deno ${Deno.version.deno}`, environment: Deno.env.get('RUNHEED_ENV') ?? 'production', request: req ? { method: req.method, url: req.url } : undefined, extra, timestamp: new Date().toISOString(), }), }); } catch { // Reporting failed; the function still answers. } } export function withRunheed(handler: (req: Request) => Response | Promise<Response>) { return async (req: Request): Promise<Response> => { try { return await handler(req); } catch (err) { await reportError(err, req); return new Response(JSON.stringify({ error: 'Internal error' }), { status: 500, headers: { 'Content-Type': 'application/json' } }); } }; }3.Wrap each function's handler. Errors you catch yourself can be sent with reportError(err, req, { anything: "useful" }).
supabase/functions/checkout/index.tstypescriptimport { withRunheed } from '../_shared/runheed.ts'; Deno.serve(withRunheed(async (req) => { const { cartId } = await req.json(); // ...your code return Response.json({ ok: true, cartId }); }));4.Deploy the functions, then connect your agent with the server URL and a sign-in (Claude Code shown; Cursor, claude.ai and ChatGPT take the same URL).
terminalbashsupabase functions deploy claude mcp add --transport http runheed https://runheed.com/api/mcp
The npm and PyPI releases are not out yet, so these commands install the same package files straight from runheed.com. Nothing else changes when the registry versions ship.
What it catches
- Any error thrown inside a wrapped Edge Function handler, with the method, URL and stack trace
- Errors you report yourself with reportError, with whatever context you pass
- The same errors from the server side of Lovable and Bolt apps that run on Supabase
What it does not do
- Errors in the browser part of the app are not captured; there is no browser SDK yet
- Errors raised inside Postgres (RLS policies, triggers, database functions) are only seen when a function receives them and throws or reports them
- Errors your code catches and turns into a response are not sent unless you call reportError
Then ask your agent
> what broke in production since the last deploy? fix the top one and resolve it with the commit hash
Common Supabase Edge Functions errors
- TypeError: fetch failed
- SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON
- TypeError: Cannot read properties of undefined (reading 'x')
- [UnhandledPromiseRejection: This error originated either by throwing inside of an async...
- psycopg.errors.UniqueViolation: duplicate key value violates unique constraint "users_e...
- Access to fetch at 'https://api.example.com/orders' from origin 'https://app.example.co...
- ZodError: [ { "code": "invalid_type", "expected": "string", "received": "undefined", "p...