Skip to content
Everframe Docs
Documentation

Crash reporting

Not every bug has a user willing to file it. Uncaught errors become reports on their own.

Updated

Crash reporting is on by default — unlike replay and body capture, the payload is data the SDK already holds (the exception plus the breadcrumb trail), fully redacted. It is a client veto, so you can switch it off:

init({ sdkKey: 'evf_live_…', crashReporting: { disabled: true } });

What is caught

Two mechanisms:

mechanismFires on
onerrorAn uncaught exception.
unhandledrejectionA promise rejection nobody handled.

Both are page-global handlers, so they catch throws from anywhere on the page that reach window.onerror: an event handler, a timer, a third-party script. An error your own code catches is handled and can be reported explicitly with captureException().

A framework’s render is covered only when the framework hands its errors to the page. React 18 and 19 do that for an error no error boundary catches: the report arrives as onerror, without React’s component stack. In a React app, use @everframe/react instead: its EverframeProvider reports that error as a fatal crash with the component stack (see React crash reporting). If you pass your own onUncaughtError to createRoot, React no longer reports the error to the page; forward it from there with captureException(). Frameworks that catch their own errors are covered below.

Report a caught exception

import { init } from '@everframe/web';

const everframe = init({
  sdkKey: 'evf_live_…',
  appVersion: '2.4.0',
  appBuild: 'web-abc123',
});

try {
  await saveCart();
} catch (error) {
  everframe.captureException(error, {
    severity: 'warning',
    context: 'checkout.save-cart',
    metadata: { attempt: 2 },
  });
}

captureException(error: unknown, options?: CaptureExceptionOptions): void reports a caught failure without opening the reporter. Options can add severity ('info', 'warning', or 'error'), a short context label, and JSON-shaped metadata for this occurrence. The SDK owns a bounded, redacted snapshot; invalid optional values are omitted without losing the core error. Details do not change handling, fatality, grouping, or retry identity.

From a React error boundary, also pass React’s componentStack, so the error page names the components the error happened in:

componentDidCatch(error: Error, info: React.ErrorInfo) {
  everframe.captureException(error, { componentStack: info.componentStack });
}

Every error also records whether the browser had translated the page. Chrome’s translation rewrites text React rendered, the most common cause of React’s NotFoundError from removeChild or insertBefore; the error page says so when the page was translated.

Capture uses the existing redaction, user context, breadcrumbs, and outbox. Returning does not confirm server receipt; queued reports use the normal delivery retry path. It accepts Error objects and other thrown values.

Explicit reports use source: 'error', mechanism: 'captureException', handled: true, and fatal: false. Global errors use handled: false and fatal: false: an unhandled browser exception does not imply process termination.

Both disabled: true and crashReporting.disabled: true suppress explicit capture. Calls after kill() or destroy() are no-ops.

Set appBuild to identify the exact deployed build. It is stored as context.app.build on errors and user-filed reports; appVersion remains the release version. To translate minified production frames, pass that same exact build ID to the private source-map uploader.

What arrives

An unattended report is an ordinary envelope with its top-level source set to error instead of manual — a web page survives an uncaught error, so crash is reserved for the native SDKs’ process-terminating case — plus a payload.crash block:

{
  "exceptionType": "TypeError",
  "message": "cart.lines is undefined",
  "frames": [
    {
      "raw": "at renderCart (cart.js:42:11)",
      "file": "cart.js",
      "function": "renderCart",
      "line": 42,
      "col": 11
    }
  ],
  "mechanism": "onerror",
  "handled": false,
  "fatal": false,
  "occurredAt": "2026-06-15T11:59:58.400Z",
  "fingerprint": "9f2c1ab30d84e177"
}

The envelope keeps the Raw frames exactly as the SDK captured them. When a private map for the report’s exact app and build is ready, Everframe stores derived mapped locations and the error detail opens in Mapped mode. Raw remains available, including when processing is pending, partial, or failed.

mechanism is a plain string, not an enum, so v2 can add values without older receivers rejecting reports. Treat unknown values as opaque.

Grouping

fingerprint is 16 hex characters computed on the client from the exception type and the top frames. It is a heuristic, deliberately — it means grouping works with no server round-trip and no symbolication step. Two crashes sharing a fingerprint are the same bug often enough to be useful for triage; treat it as a strong hint, not an identity.

That client fingerprint remains in the report and groups the error until its build’s source maps are uploaded. Then Everframe groups it by function: the first frame in your own code, by original file and function (with the functions or classes it sits in, read from the source text in your maps), so the same error keeps one issue across builds even when an edit moves it. An anonymous function, or one the map’s source text cannot identify, keys its exact original line and column instead. There is nothing to configure. The maps are uploaded by @everframe/bundler-plugin or @everframe/cli. See Error triage & releases.

Duplicate reports and limits

Reporting the same error object through multiple capture paths creates one report per SDK instance. The first accepted capture determines classification: calling captureException(error) and then rethrowing that object leaves the report marked handled.

Explicit and automatic capture have separate allowances. Each accepts one report per fingerprint and up to ten reports per SDK instance, so explicit reports cannot exhaust the automatic allowance. Different error objects with the same fingerprint can still be throttled; occurrence counts reflect received reports, not every failure. Retries keep the same report ID and do not add occurrences on the server. Automatic error breadcrumbs still record throttled errors.

What comes with it

An unattended report’s title is generated from the exception — TypeError: cart.lines is undefined — and its description is empty; nobody typed one. What makes it useful is everything else the SDK already had: the breadcrumb trail leading to the throw, the console and network breadcrumbs, the route, and available session context. Error capture does not acquire a screenshot or attach replay; it uses the context already buffered.

Errors your framework swallows

Vue’s errorHandler, Svelte’s error boundaries and Angular’s ErrorHandler all catch errors before they reach window.onerror. Those never become unattended reports automatically. Forward a caught error from inside that handler:

everframe.captureException(err);

Or call everframe.open() from there to put a human in the loop.