Cloudflare Workers error tracking your coding agent can read
When a Worker throws, the visitor gets an error page and the stack trace is only in the live tail or Workers Logs. One small module wraps your fetch handler and sends each exception to Runheed with fetch. It reports inside ctx.waitUntil, so the response is not held up, and there is no package to install. Your coding agent then reads the error, the request and the stack trace over MCP.
Set it up
1.Store the pk_... part of your project DSN as a secret.
terminalbashnpx wrangler secret put RUNHEED_KEY2.Add one module next to your Worker.
src/runheed.tstypescriptexport interface RunheedEnv { RUNHEED_KEY?: string; RUNHEED_ENV?: string; } type FetchHandler<E> = (request: Request, env: E, ctx: ExecutionContext) => Response | Promise<Response>; 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].includes('node_modules') }]; }); } export async function reportError(err: unknown, env: RunheedEnv, request?: Request, extra: Record<string, unknown> = {}) { if (!env.RUNHEED_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': env.RUNHEED_KEY }, body: JSON.stringify({ errorType: e.name || 'Error', errorMessage: e.message || String(err), stacktrace: frames(e.stack), platform: 'cloudflare-workers', runtime: 'workerd', environment: env.RUNHEED_ENV ?? 'production', request: request ? { method: request.method, url: request.url } : undefined, extra: { colo: (request as { cf?: { colo?: string } } | undefined)?.cf?.colo, ...extra }, timestamp: new Date().toISOString(), }), }); } catch { // Reporting failed; the Worker still answers. } } export function withRunheed<E extends RunheedEnv>(handler: FetchHandler<E>): FetchHandler<E> { return async (request, env, ctx) => { try { return await handler(request, env, ctx); } catch (err) { ctx.waitUntil(reportError(err, env, request)); return new Response('Internal error', { status: 500 }); } }; }3.Wrap the fetch handler. Errors you catch yourself can be sent with ctx.waitUntil(reportError(err, env, request, { anything: "useful" })).
src/index.tstypescriptimport { withRunheed, type RunheedEnv } from './runheed'; interface Env extends RunheedEnv { // your bindings (KV, D1, R2...) } export default { fetch: withRunheed<Env>(async (request, env, ctx) => { const url = new URL(request.url); // ...your code return Response.json({ ok: true, path: url.pathname }); }), } satisfies ExportedHandler<Env>;4.Deploy, then connect your agent with the server URL and a sign-in (Claude Code shown; Cursor, claude.ai and ChatGPT take the same URL).
terminalbashnpx wrangler 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 exception thrown inside the wrapped fetch handler, with the method, URL, Cloudflare data center and stack trace
- Errors you report yourself with reportError, with whatever context you pass
What it does not do
- scheduled, queue and email handlers need their own try/catch with reportError
- Exceeded CPU or memory limits end the Worker before any code can report them
- Stack traces point at lines in the bundled Worker script that Wrangler uploads, not your source files
- Browser errors are not captured; there is no browser SDK yet
Then ask your agent
> what broke in production since the last deploy? fix the top one and resolve it with the commit hash
Common Cloudflare Workers errors
- TypeError: fetch failed
- TypeError: Failed to parse URL from /api/orders
- 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...
- ZodError: [ { "code": "invalid_type", "expected": "string", "received": "undefined", "p...
- Access to fetch at 'https://api.example.com/orders' from origin 'https://app.example.co...