Bun (Sentry-compatible SDK)
Use the official Sentry SDK for Bun with a Belvador DSN, Belvador's scrubbing and an on-disk queue while your node is unreachable.
On this page
For Bun servers Belvador uses the official Sentry SDK for Bun with your Belvador DSN and Belvador's own scrubbing from @belvador/core/sentry. Add @belvador/spool and errors that cannot be sent wait on disk until your node answers, also across a crash or a restart.
1. Install#
bun add @sentry/bun@11.9.0 @belvador/core@0.6.12. Create instrument.ts#
import * as Sentry from "@sentry/bun";import { scrubBreadcrumb, scrubEvent } from "@belvador/core/sentry";Sentry.init({ dsn: process.env.BELVADOR_DSN ?? "https://<public-key>@ingest.belvador.dev/<project-id>", release: process.env.APP_VERSION, environment: process.env.NODE_ENV, sendDefaultPii: false, tracesSampleRate: 0, maxBreadcrumbs: 50, maxValueLength: 8192, // Belvador's scrubbing: request bodies, cookies and credentials never leave the app. beforeSend: scrubEvent, beforeBreadcrumb: scrubBreadcrumb,});The options are the same as for Node.js: no personal data or spans from the SDK, Belvador's bounds, and beforeSend: scrubEvent / beforeBreadcrumb: scrubBreadcrumb — the scrubbing Belvador's own SDKs run, so request bodies, cookies, auth headers and credentials in text never leave your server.
3. Load it before your app#
bun --preload ./instrument.ts server.ts--preload runs the file before your entry point, so Bun.serve is instrumented when your app starts it.
Errors in Bun.serve#
// An error a Bun.serve handler throws is reported by @sentry/bun; the client gets a 500.Bun.serve({ port: Number(process.env.PORT ?? 3000), async fetch(req) { const url = new URL(req.url); if (url.pathname === "/invoices") return Response.json(await listInvoices()); return new Response("Not found", { status: 404 }); },});An error your fetch handler throws is reported by @sentry/bun and the client gets a 500. Errors you handle yourself: Sentry.captureException(error).
Keep errors when your node is unreachable#
import * as Sentry from "@sentry/bun";import { scrubBreadcrumb, scrubEvent } from "@belvador/core/sentry";import { withSpool } from "@belvador/spool";Sentry.init({ dsn: process.env.BELVADOR_DSN ?? "https://<public-key>@ingest.belvador.dev/<project-id>", release: process.env.APP_VERSION, environment: process.env.NODE_ENV, sendDefaultPii: false, tracesSampleRate: 0, maxBreadcrumbs: 50, maxValueLength: 8192, // Belvador's scrubbing: request bodies, cookies and credentials never leave the app. beforeSend: scrubEvent, beforeBreadcrumb: scrubBreadcrumb, // Errors that cannot be sent wait on disk and go out after a restart. transport: withSpool(Sentry.makeFetchTransport, { dir: process.env.BELVADOR_SPOOL_DIR ?? "./.belvador-spool", }),});Errors wait in BELVADOR_SPOOL_DIR (at most 64 MiB, 72 hours) and are sent by this process or the next one once your node answers. Details: Offline and durable delivery.
Database errors#
{ data, error } results from Supabase or PostgREST: bind captureResult to @sentry/bun as shown in Database errors, with import * as Sentry from "@sentry/bun".
Set it up with an AI agent#
Paste this into Claude Code, Cursor or another coding agent. It contains the files above and the rules it must follow:
Belvador errors: Bun (@sentry/bun)Tested with: @sentry/* 11.9.0Guide: https://belvador.dev/en/docs/sdk/bunGoalReport this server's errors to Belvador with the official Sentry SDK and Belvador's scrubbing, exactly as in the files below. Keep any other error SDK out: one Sentry.init per process.RulesCopy the files as they are and keep every Sentry.init option: sendDefaultPii: false, tracesSampleRate: 0, maxBreadcrumbs: 50, beforeSend: scrubEvent, beforeBreadcrumb: scrubBreadcrumb. Read the DSN from BELVADOR_DSN; do not invent other environment variables. Do not change authentication, routing or business logic. Report errors the code already handles with captureException (report() on Supabase Edge Functions) and wrap Supabase or PostgREST results that return an error instead of throwing with captureResult, without changing what the code does with the result.```bashbun add @sentry/bun@11.9.0 @belvador/core@0.6.1``````bashnpm install @belvador/spool@0.6.1```instrument.ts```typescriptimport * as Sentry from "@sentry/bun";import { scrubBreadcrumb, scrubEvent } from "@belvador/core/sentry";import { withSpool } from "@belvador/spool";Sentry.init({ dsn: process.env.BELVADOR_DSN ?? "https://<public-key>@ingest.belvador.dev/<project-id>", release: process.env.APP_VERSION, environment: process.env.NODE_ENV, sendDefaultPii: false, tracesSampleRate: 0, maxBreadcrumbs: 50, maxValueLength: 8192, // Belvador's scrubbing: request bodies, cookies and credentials never leave the app. beforeSend: scrubEvent, beforeBreadcrumb: scrubBreadcrumb, // Errors that cannot be sent wait on disk and go out after a restart. transport: withSpool(Sentry.makeFetchTransport, { dir: process.env.BELVADOR_SPOOL_DIR ?? "./.belvador-spool", }),});``````bashbun --preload ./instrument.ts server.ts```VerifyStart the app locally with BELVADOR_DSN set and trigger one test error (a route or a one-off script that throws). Confirm it appears as an issue in the Belvador project. Do not send test errors to production unless the user says so.ReportList the files you changed, the packages added with exact versions, the environment variables the app now needs, the test error you saw arrive, and anything you could not verify.Limits to know#
An uncaught error still ends the process, as Bun does without an SDK; the error is sent first.
An error thrown in a
Bun.servehandler is reported and answered with a 500; the server keeps answering. Bun 1.4.4 still ends such a process with exit code 1 when it stops — with or without an SDK — so a process manager may count it as a failure.Belvador has not checked Bun framework integrations (Elysia, Hono) one by one; errors they turn into responses themselves need
Sentry.captureException.Keep
tracesSampleRate: 0; for traces use OpenTelemetry.
Spotted something wrong or missing? Tell us: hello@belvador.dev