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:
<EverframeProvider config={{ sdkKey: 'evf_live_…', crashReporting: { disabled: true } }}>
What is caught
On the web, two mechanisms:
mechanism | Fires on |
|---|---|
onerror | An uncaught exception, and an error no error boundary catches while rendering, in an effect or in a lifecycle method. |
unhandledrejection | A promise rejection nobody handled. |
Errors no boundary catches
When a component throws while rendering (or in an effect or a lifecycle
method) and no error boundary inside EverframeProvider catches it, React
unmounts the whole tree and the page goes blank. EverframeProvider reports
that error as a fatal crash: source: 'crash', mechanism: 'onerror',
handled: false, fatal: true, with React’s component stack after the
message:
cart.lines is undefined
This error is located at:
at CartRow (https://shop.example/assets/index.js:1:20470)
at section
at Cart (https://shop.example/assets/index.js:1:20890)
No error boundary is needed. The report is sent at once and kept in the outbox
until it is accepted, so a failed send goes out on the next page load. After
reporting, the provider hands the same error on: an error boundary of yours
around the provider still catches it, and otherwise React reports it to the
page as before (window.onerror sees it once more; it is not reported twice).
Errors that a boundary inside the provider catches are not reported
automatically; see React error boundaries.
A React 18 development build first reports a failed render or effect to
window.onerror itself, so in development such an error can arrive as a
non-fatal onerror error, instead of or as well as the fatal report.
Production builds of React 18 and 19 send the one fatal report.
Report a caught exception
Inside a component, use the hook:
import { useEverframe } from '@everframe/react';
const { captureException } = useEverframe();
async function save() {
try {
await saveCart();
} catch (error) {
captureException(error, {
severity: 'warning',
context: 'checkout.save-cart',
metadata: { attempt: 2 },
});
}
}
Outside a component, import captureException from @everframe/react. It routes
to the mounted provider and is a no-op before mount and after unmount.
The signature is
captureException(error: unknown, options?: CaptureExceptionOptions): void.
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.
Capture reports without opening UI and reuses redaction, user context,
breadcrumbs, and the outbox. Returning does not confirm server receipt;
delivery uses the existing retries. Both disabled and
crashReporting.disabled suppress capture, as does kill().
Explicit errors use mechanism: 'captureException', handled: true, and
fatal: false. Global errors use handled: false and fatal: false because an
unhandled browser error does not imply process termination. The exception is
an error no boundary catches, which takes the app down with it: fatal: true.
Set appVersion and appBuild in the provider config to identify the release
and exact deployed build. appBuild appears in context.app.build for errors
and user-filed reports. 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
top-level source set to error instead of manual — a web page survives an
uncaught error — or to crash for an error no boundary catches — plus a
payload.crash block:
{
"exceptionType": "TypeError",
"message": "cart.lines is undefined",
"frames": [
{
"raw": "at Cart (cart.tsx:42:11)",
"file": "cart.tsx",
"function": "Cart",
"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
The same error object is reported once per SDK instance across hook, top-level, and global capture. The first accepted capture determines classification. If you report a caught error and rethrow the same object, its report stays handled.
Explicit and automatic capture each accept one report per fingerprint and ten reports per SDK instance. Their allowances are independent. Distinct error objects with the same fingerprint can still be throttled, so counts describe received reports rather than every failure. Transport retries preserve the report ID and do not add server occurrences.
What comes with it
An error report has a title generated from the exception and an empty description. It includes what the SDK already had in memory: the breadcrumb trail leading to the throw, with the console and network entries on it, plus the route, device and app. There is no screenshot and no replay — nothing was frozen, because nobody opened the reporter. That is still the difference between a stack trace and a timeline.
React error boundaries
An error caught by a boundary is handled: the boundary decides what the user
sees, so it is not reported automatically. Forward it explicitly from the
boundary’s componentDidCatch method:
import { captureException } from '@everframe/react';
// In your error boundary class:
componentDidCatch(error: Error, info: React.ErrorInfo) {
captureException(error, { componentStack: info.componentStack });
}
Pass info.componentStack along. It goes under the error’s message, and the
error page names the components the error happened in, read through your source
map. That matters most for errors React throws itself, such as
NotFoundError: Failed to execute 'removeChild' on 'Node': their stack holds
only React’s own code. The title and grouping stay the same.