Core Features
Error Tracking
How Reliable captures, groups, enriches, and de-duplicates errors, both automatic and manual.
Automatic capture#
With captureErrors: true (the default), Reliable installs two listeners:
window.addEventListener('error'): uncaught synchronous errors and resource load failures.window.addEventListener('unhandledrejection'): Promise rejections that no.catch()handled.
Both listeners normalize the thrown value into a common shape, fingerprint it, attach context, and send a POST /errors request.
What gets attached to every error#
| Name | Type | Default | Description |
|---|---|---|---|
| fingerprint | string | — | Hash of (message + first stack frame). The SDK uses it to drop repeats within 5 seconds; the backend groups errors on its own key (see Grouping below). |
| stack_trace | string | — | The raw stack from error.stack. Source-map resolution happens server-side. |
| trigger | enum | — | page_load, click, navigation, api_response, timer, or unknown, heuristic for what *caused* the error. |
| is_crash | boolean | — | True for unrecoverable errors: React error boundaries, ChunkLoadError, hydration failures. |
| severity | enum | — | low | medium | high | critical. Crashes are critical; unhandled rejections are high; everything else is medium. |
| router_history | array | — | The last 10 route changes the user made before the error. |
| browser_state | object | — | Cookies + localStorage at the time of the error (with sensitive keys redacted). |
| breadcrumbs | array | — | The last 30 breadcrumbs, clicks, navigations, console logs, manual additions. |
| tags | object | — | Per-event tags from captureException options, merged over scope tags from setTag/setTags. |
| component_stack | string | — | Set by the React error boundary. Identifies which component crashed in the React tree. |
Grouping#
The backend groups errors by the message plus the top stack frame, with everything that changes between deploys and browsers removed first: the domain, query string, line and column, hashed bundle file names, build-id folders, and minified function names. So the same bug is one group, and one incident, whether it happens on www. or the bare domain, before or after a deploy, and whichever browser's stack format it arrives in. Readable function names are kept, so one message thrown from two different functions stays two groups. The message itself is compared as sent: errors you throw with your own message group across browsers, but the browser's built-in wording differs (Chrome's Cannot read properties of undefined is Safari's undefined is not an object), so those stay one group per browser.
De-duplication#
Errors with the same fingerprint within a 5-second window are dropped client-side. This stops render-loop floods (an error in a render that retriggers itself) from saturating the queue and racking up cost. The first error in a burst is always sent, you'll never miss the initial signal.
On the server, an error whose incident is still open is counted on that incident instead of opening a new one, and does not page again. Frontend incidents resolve after 60 minutes without new occurrences, so a later repeat pages afresh. See Alert Routing.
Manual capture#
For caught errors, soft failures, or any error path you handle yourself, use captureException:
import { captureException } from '@reliableapp/frontend-core';
try {
await chargeCustomer(amount);
} catch (err) {
captureException(err, {
severity: 'high',
isCrash: false,
tags: {
flow: 'checkout',
payment_provider: 'stripe',
amount_usd: String(amount),
},
});
showRetryDialog();
}captureException works with any thrown value, not just Error instances:
captureException('user clicked submit twice'); // string
captureException({ code: 'EBADCSRF', detail: '…' }); // POJO
captureException(new TypeError('expected number')); // ErrorStrings and POJOs are normalized into a synthetic Error internally so the backend can still group and replay them properly.
captureMessage for non-error events#
Sometimes you want to report something that isn't an exception, a soft failure, a retry that succeeded, a state transition worth knowing about.
import { captureMessage } from '@reliableapp/frontend-core';
if (retryCount > 0) {
captureMessage('payment retry succeeded', {
severity: 'low',
tags: { retry_count: String(retryCount) },
});
}Severity levels#
| Name | Type | Default | Description |
|---|---|---|---|
| low | string | — | Soft signals, retries, recoverable errors, noteworthy state. Don't page anyone. |
| medium | string | — | Default for caught exceptions. Worth attention but not urgent. |
| high | string | — | Unhandled rejections, payment failures, anything that hurts a user. |
| critical | string | — | Crashes, React error boundary, chunk load failure, hydration failure. Page someone. |
Return value#
Both captureException and captureMessage return a UUID string (the event ID) or null if the event was dropped (SDK not initialized, de-duped, or filtered by beforeSend).
const eventId = captureException(err);
if (eventId) {
console.log('Reported as event', eventId);
// Show user a "report this with ID xyz" dialog
}React error boundaries
<ReliableErrorBoundary>. It catches render-phase crashes and reports them with the full React component stack, see React → Error Boundary.