Skip to content

Verify the connection

Prove that every runtime of your app reports errors to the right project: verifyIntegration(), a tagged error for any SDK, diagnostic codes and connection problem codes.

Markdown
On this page

“No errors” can mean a healthy app — or an app whose errors never reach Belvador. A connection check proves, runtime by runtime, that errors from your app arrive in the right project. It sends one or two marked errors that never become issues, and Belvador shows the result on the project's Connection tab.

Why a first issue is not enough#

A first issue proves that one error arrived once. It does not prove that:

  • the server side reports too, not only the browser;

  • uncaught errors (the ones nobody wrapped in try … catch) are reported;

  • your server code runs inside Belvador's middleware, so errors are seen before an error page swallows them;

  • the app still uses an active DSN key after a deploy.

The check covers each of these and keeps the result, so the team can see when it was last proved.

What the Connection tab shows#

Open a project and choose Connection. For each runtime the platform needs — Browser, Server, or both for TanStack Start — you see:

StateMeaning
VerifiedA check from this runtime arrived and every part it can prove passed
Not verifiedNo check has arrived in the last 30 days — the app may or may not be reporting
Problem (red)Errors are being rejected or lost right now
Problem (amber)Errors arrive, but part of the app is not covered — for example server code outside the middleware

Open problems are listed first with what to do. Problems close by themselves once the cause is gone; the alert rule SDK connection problems sends one message when a problem starts and one when it is fixed — see Alerts.

Run the check with verifyIntegration()#

From @belvador/* 0.2.0, one call runs every check for the runtime it is called from and reads the result back from Belvador. It never throws and never runs by itself — run it once after setup and after deploys that touch error handling, not on every page load.

Browser#

TypeScript
import { verifyIntegration } from "@belvador/react";// Run once after setup or deploy (for example behind a debug flag), never on every page load.// The console shows one expected "Belvador verification (uncaught)" error — that is the test.const result = await verifyIntegration();// result.outcome: "verified" | "problem" | "unconfirmed"; the console prints one summary line.

The same call exists in @belvador/browser and @belvador/tanstack-start/client. The browser console shows one expected error, “Belvador verification (uncaught, expected)”: the check throws it on purpose to prove that uncaught errors are reported. Pass { uncaught: false } to skip that part; the browser then stays delivery only.

Server (TanStack Start)#

src/routes/api/verify-belvador.ts
import { createFileRoute } from "@tanstack/react-router";import { verifyIntegration } from "@belvador/tanstack-start/server";// Temporary: open /api/verify-belvador once after deploying, then delete this file.// It must run inside belvadorRequestMiddleware (src/start.ts) — that is part of the check.export const Route = createFileRoute("/api/verify-belvador")({  server: {    handlers: {      GET: async () => Response.json(await verifyIntegration()),    },  },});

Open /api/verify-belvador once after deploying, read the JSON, then delete the file. The check fails with BLV_HOOK_MISSING if the route does not run inside belvadorRequestMiddleware.

Cloudflare Workers#

src/index.ts
import { verifyIntegration } from "@belvador/cloudflare";// Inside the fetch handler wrapped by withBelvador. Temporary: remove after the check.if (new URL(request.url).pathname === "/__belvador/verify") {  return Response.json(await verifyIntegration());}

Reading the result#

verifyIntegration() resolves to an object and prints one line such as:

Text
[belvador] verify server: problem — hook missing (BLV_HOOK_MISSING), delivery confirmed — bv1_3f0c… — https://belvador.dev/en/docs/sdk/verify#blv-hook-missing
FieldValues
outcomeverified — every check confirmed by Belvador; problem — something failed (see checks); unconfirmed — sent, but Belvador could not confirm yet (node offline, read-back off or timed out)
checks[].checkinit, hook, delivery, uncaught (browser)
checks[].statusconfirmed, accepted (sent, not yet read back), missing (sent, never arrived), failed (with a code), skipped
verificationIdbv1_ + 32 hex characters; the same id appears on the Connection tab
docsUrlThe section of this page for the first code found

Options: timeoutMs (default 20000 ms, at most 30000), uncaught (browser, default true), readback (default true). One call sends at most 2 events and asks Belvador at most 10 times.

Any SDK: send a tagged error#

Every Sentry-compatible SDK can run the check — and so can @belvador/* 0.6.1. Send one error with the tag belvador.verification set to bv1_ followed by 32 lowercase hex characters, then read it back.

Belvador packages before 0.2.0#

TypeScript
import { captureException, flush, withScope } from "@belvador/react";// One-time connection check: a marked error that never becomes an issue.const id = "bv1_" + crypto.randomUUID().replaceAll("-", "");withScope((scope) => {  scope.setTag("belvador.verification", id);  captureException(new Error("Belvador verification"));});await flush(5000);console.log("[belvador] verification id:", id);

Node.js#

JavaScript
import * as Sentry from "@sentry/node";// One-time connection check: a marked error that never becomes an issue.const id = "bv1_" + crypto.randomUUID().replaceAll("-", "");Sentry.withScope((scope) => {  scope.setTag("belvador.verification", id);  Sentry.captureException(new Error("Belvador verification"));});await Sentry.flush(5000);console.log("[belvador] verification id:", id);

Python#

Python
import uuidimport sentry_sdk# One-time connection check: a marked error that never becomes an issue.verification_id = "bv1_" + uuid.uuid4().hexwith sentry_sdk.new_scope() as scope:    scope.set_tag("belvador.verification", verification_id)    sentry_sdk.capture_exception(Exception("Belvador verification"))sentry_sdk.flush(5)print("[belvador] verification id:", verification_id)

Read it back#

Terminal
curl -s "https://ingest.belvador.dev/api/<project-id>/verification/bv1_<id printed by your app>/?sentry_key=<public-key>"

Use the project number and public key from the DSN. The answer lists the checks that reached your node:

JSON
{ "v": 1, "verification_id": "bv1_…", "node": "connected", "checks": [{ "surface": "server", "check": "delivery", "hook": "unknown" }] }

A tagged error proves the delivery path only: a plain Sentry SDK cannot say whether its error hooks run, so the Connection tab shows delivery only for that runtime.

SDK diagnostic codes#

The SDK reports these locally — in the browser console with debug: true, or to your own onDiagnostic callback — at most once a minute per code. They are never sent to Belvador.

TypeScript
init({  dsn: "https://<public-key>@ingest.belvador.dev/<project-id>",  onDiagnostic: (d) => console.warn(d.code, d.count, d.status),});// getDiagnostics() returns a snapshot: active, SDK name, DSN host + project + key hint, last send, counts.

BLV_DSN_MISSING#

init() got no DSN, so the SDK is off. Copy the DSN from the project page.

BLV_DSN_INVALID#

The DSN is not a Belvador DSN (https://<32 hex>@host/<project>). The SDK is off. Copy it again; do not add query strings.

BLV_INIT_CONFLICT#

Another error-tracking client already runs in the page, so Belvador did not start. Remove the old Sentry.init so errors are not reported twice.

BLV_NOT_INITIALIZED#

captureException ran while the SDK was off — before init(), or after init() failed. Call init() as early as possible.

BLV_SEND_UNAUTHORIZED#

Belvador refused the DSN key (revoked, expired or another project's). The SDK stops sending until the page or Worker reloads. Use an active key from Project settings → DSN keys.

BLV_SEND_RATE_LIMITED#

The project's rate limit or the monthly quota was reached; events are dropped until the wait (retryAfterS) ends. See intake limits.

BLV_SEND_REJECTED#

One event was not accepted — usually too large. Keep extra data and attachments small.

BLV_SEND_NETWORK#

Belvador could not be reached after retries. Check the network, ad blockers and that your Content-Security-Policy connect-src allows https://ingest.belvador.dev.

BLV_SEND_SERVER#

Belvador answered with a server error after retries. Usually temporary; if it continues, check the status of your node.

BLV_QUEUE_FULL#

Too many events waited to be sent at once, so one was dropped. Reduce bursts of repeated errors with ignoreErrors or sampleRate.

BLV_EVENT_TOO_LARGE#

An event or attachment is over the size limit and was dropped before sending — see SDK configuration.

BLV_HOOK_MISSING#

The check ran in server code that is not inside Belvador's middleware or Worker wrapper, so errors thrown there are not reported automatically. Register belvadorRequestMiddleware and belvadorFunctionMiddleware last in src/start.ts — see TanStack Start — or wrap the Worker with withBelvador.

BLV_DROPPED_BY_APP#

The verification never left the app: your beforeSend returned null, ignoreErrors matched it, or sampleRate below 1 dropped it. Let errors named BelvadorVerificationError through.

BLV_MONITOR_INVALID_SLUG#

A monitor slug must be lowercase letters, digits, - and _, at most 64 characters (for example nightly-invoices). The job still ran; no check-in was sent for it — see Monitors.

BLV_MONITOR_INVALID_REASON#

A reason passed to run.skip(), run.unknown() or captureCheckIn must be a short code such as provider_timeout, not a message. It was left out of the check-in.

BLV_MONITOR_CONFIG_INVALID#

The schedule or a setting passed to withMonitor is not valid (a crontab without exactly 5 fields, an unknown time zone, a number out of range). The check-in was sent without it, so the monitor's settings did not change.

BLV_CHECK_IN_DROPPED#

A check-in was not sent: too many were waiting at once (at most 10 of the 30 queued sends), check-ins were rate limited, or the network failed. Errors are not affected. If it repeats, a monitor may show as missed — check how often the job reports.

BLV_TRACE_TARGET_INVALID#

An entry in tracePropagation.targets is not a string or a regular expression and was ignored; requests to it get no trace headers.

BLV_OTEL_INVALID#

The otel.api option is not the @opentelemetry/api module (pass import * as api from "@opentelemetry/api", the same copy your OpenTelemetry SDK uses). The bridge stays off; errors are still captured, without the span's ids.

BLV_TRACE_OWNER_OTEL#

Both otel and tracePropagation are set. OpenTelemetry already propagates the trace, so Belvador adds no trace headers of its own and tracePropagation is ignored. Remove tracePropagation to silence this.

BLV_PRIVACY_POLICY_INVALID#

From SDK 0.6.0. privacyPolicy() got JSON that is not a valid policy from the project's Privacy page (wrong schema, an unsafe pattern, a bad selector). The app sends without project rules; your node still applies the project's current policy before storing anything. Copy the policy again with "Copy as code".

BLV_PREVIEW_ONLY#

From SDK 0.6.0. Preview mode is on (preview: true or BELVADOR_PREVIEW=1): each envelope is printed with every rule and scrubber applied, and nothing is sent. A verification in preview mode never reaches Belvador. Turn preview off to send.

BLV_OFFLINE_UNAVAILABLE#

From SDK 0.6.0. offline: true is set, but this browser has no usable IndexedDB (some private windows, a blocked storage policy) or the offline module could not load (a strict CSP without the app's own chunks). Errors are still sent, without an offline queue: one that fails on the network is lost. See Offline and delivery.

Connection problem codes#

Belvador opens these from what it sees at the edge and from checks. Each one names the project and, when it applies, the runtime.

INTEGRATION_KEY_INACTIVE#

Your app still sends with a revoked or expired DSN key; those errors are rejected. Put an active DSN in the app and deploy again.

INTEGRATION_ORIGIN_BLOCKED#

Errors come from a website that is not in the project's Allowed origins. Add the domain, or empty the list to accept every domain.

INTEGRATION_RATE_LIMITED#

Many errors are dropped by the project's rate limit. Raise it in Project settings or reduce repeated errors.

INTEGRATION_QUOTA_EXCEEDED#

The workspace's monthly quota is used up; errors are dropped until the next month.

INTEGRATION_TOO_LARGE#

Several payloads were too large. Turn off large attachments and keep extra data small.

INTEGRATION_INVALID_EVENTS#

The node cannot read some payloads — usually an unsupported SDK or a custom transport.

INTEGRATION_BUFFER_LOSS#

The node was offline long enough that waiting errors expired or the waiting room filled up. Bring the node back from the Node page.

INTEGRATION_SDK_DROPS#

The SDK in your app reported that it could not send errors (network, CSP or a full queue). Turn on debug to see the reason in the console.

INTEGRATION_HOOK_MISSING#

A server check ran outside Belvador's middleware — see BLV_HOOK_MISSING above.

INTEGRATION_UNCAUGHT_MISSING#

A browser check announced an uncaught error that never arrived within 2 minutes: something replaces window.onerror or init() runs too late.

Limits#

  • A check proves the path it ran on, at that moment. Code paths that catch errors themselves still need captureException.

  • The uncaught browser error is thrown in a setTimeout on purpose; you may see it in the console and in browser extensions that watch errors.

  • Checks are limited per project (60 an hour) and per key (30 read-backs a minute). Verification events never count as issues and are kept as metadata for 30 days.

  • Your node needs version 0.1.17 or newer to recognise checks; older nodes show a banner on the Connection tab.

Posts on this topic

All posts

Spotted something wrong or missing? Tell us: hello@belvador.dev