Skip to content

Framework guide

Error tracking for Cloudflare Workers

@belvador/cloudflare wraps a Worker so errors in its handlers are reported while responses stay exactly as your code returns them. Each invocation gets its own scope, so user, tags and context never leak between requests.

Start freeCloudflare Workers

Updated Oct 25, 2026

The short answer#

  • Wrap the exported Worker with withBelvador from @belvador/cloudflare.

  • Errors in fetch, scheduled, queue, email and tail handlers are reported; responses are unchanged.

  • Sending happens in ctx.waitUntil after the response.

  • Requires the nodejs_compat flag; Durable Object classes are not wrapped in 0.x.

Install#

Terminal
npm install @belvador/cloudflare@0.6.1

Wrap the Worker#

src/index.ts
import { withBelvador } from "@belvador/cloudflare";type Env = { BELVADOR_DSN?: string; BELVADOR_RELEASE?: string };// Requires the nodejs_compat compatibility flag.// The DSN comes from env.BELVADOR_DSN when you leave it out here.export default withBelvador(  (env: Env) => ({    dsn: env.BELVADOR_DSN ?? "https://<public-key>@ingest.belvador.dev/<project-id>",    release: env.BELVADOR_RELEASE,  }),  {    async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {      return new Response("ok");    },  },);

Enable the nodejs_compat compatibility flag in wrangler.jsonc; it is what gives every invocation its own scope (Cloudflare guide).

Scheduled handlers#

A Worker's scheduled handler is a cron job like any other. Report it with a check-in so a run that never fires opens an incident:

src/index.ts
import { withBelvador, withMonitor } from "@belvador/cloudflare";export default withBelvador((env: Env) => ({ release: env.BELVADOR_RELEASE }), {  async scheduled(controller, env, ctx) {    await withMonitor("nightly-invoices", async (run) => {      // Your job. Nothing to do? run.skip("nothing_due")    }, { schedule: { type: "crontab", value: "0 2 * * *" }, timezone: "UTC" });  },});

See cron monitoring for schedules, results and alerts.

Traces and logs#

Workers can export OpenTelemetry traces and logs to Belvador; the guide covers Cloudflare's built-in export and the SDK route (OpenTelemetry for Workers).

Not tested yet: this setup follows the vendor's documentation; Belvador has not run it end to end.

Limits#

  • Module syntax (export default { fetch … }) deployed with Wrangler. Service-worker syntax is not supported.

  • withBelvador wraps the fetch, scheduled, queue, email and tail handlers of the exported object. Durable Object classes are not wrapped in 0.x; call captureException in them.

  • For TanStack Start or Lovable apps, use the TanStack Start package, which includes this one.

Questions#

Does wrapping change my responses or add latency to them?#

Responses are returned exactly as your code builds them. Sending finishes after the response, in ctx.waitUntil, so it does not delay the visitor.

Try it on your own Hetzner project

Free during the beta. Hetzner bills the server; Belvador never bills per event.