Crossdeck University
Watch — what's caught for free, and what you report yourself Film in production
0:00 / 0:00
Lesson 1 of 5 · Errors

Capture errors

Uncaught errors are captured the moment you install the SDK. The ones your code catches are invisible until you report them — here's what's automatic, and the one call that covers the rest.

5 min Web

When you're done: every crash and every error you care about lands in Issues, attributed to the right user.

1 What this is & why it matters

Uncaught is automatic; caught is yours to report

At init(), Crossdeck installs global handlers for uncaught exceptions, unhandled promise rejections, and a wrap around fetch (plus XHR on web). So crashes report themselves — you don't lift a finger. One sensible boundary: HTTP capture fires only on 5xx and network failures; a 4xx is never auto-reported, because a bad request usually isn't your bug.

The gap the global handlers can't see is the error your own code catches. The moment you write try/catch, that error is invisible to them — handled, swallowed, gone. Reporting those is the one thing this lesson adds, and it's a single call.

2 How to report what you catch

captureError for exceptions, captureMessage for signals

Inside a catch, hand the error to Crossdeck — optionally with context, tags, and a level:

try {
  await chargeCard();
} catch (err) {
  Crossdeck.captureError(err, { context: { plan: "pro" }, tags: { flow: "checkout" } });
}

// for a non-exception signal — a deprecated path, a slow branch:
Crossdeck.captureMessage("entered legacy checkout path", "warning");

There are three levels — error, warning, info. If you're migrating from Sentry, its fatal, debug, and log are silently coerced to error, so existing code keeps working.

3 How it works, piece by piece

Attributed, enriched, and safe by default

A captured error doesn't travel alone. It carries the context that was set when it fired — your tags, named context blocks, the last 50 breadcrumbs, and the identity known at capture time. That last point is the one to remember: call identify() first, or the error is attributed to the anonymous ID and you lose the "which customer hit this?" answer.

Under the hood, errors ride the same durable queue as your analytics — persisted, retried, and rate-limited per fingerprint so one hot bug can't flood you. Sends are gated by consent.errors, and the call never throws, even if the SDK isn't initialised yet — so wrapping your code in capture calls can never itself become a source of crashes.

4 The green result in your dashboard

Crashes and caught errors, both in Issues

Throw an uncaught error and it appears in Issues on its own. Wrap a risky call in captureError and the handled failures show up too — each attributed to the user who hit it, with its context attached.

app.cross-deck.com · issues
captured · attributed

A checkout failure on user_847, level error, tagged flow: checkout — context and identity attached, ready to triage.

Uncaught exceptions, rejections, and 5xx/network failures capture automatically; 4xx never does. Report what you catch with captureError(err, options) and non-exception signals with captureMessage(msg, level). Three levels, identity-attributed (so identify() first), durable, rate-limited, never throws.