Events survive worker death
Every event is written to chrome.storage.local before any send is
attempted, and cleared only once the server acknowledges it. A worker killed
mid-flush loses nothing — the next boot picks the queue back up.
An MV3 service worker is killed roughly 30 seconds after it goes idle, with no reliable teardown event. Anything held in memory dies with it — which is exactly when the interesting failures happen.
Sentry’s browser SDK assumes a page with a window and a DOM. A service worker
has neither. So the errors you most need to see are the ones a generic tracker
is structurally incapable of reporting.
Events survive worker death
Every event is written to chrome.storage.local before any send is
attempted, and cleared only once the server acknowledges it. A worker killed
mid-flush loses nothing — the next boot picks the queue back up.
It tells you what died
Wrap async work in track(). If Chrome kills the worker while it’s
outstanding, the next boot reports the task by name — turning “my extension
randomly does nothing” into a line item.
Host grouping
Content script errors carry the page host, so a selector that breaks when a site redesigns is one issue on one host — not noise scattered across every install.
Other sites' bugs stay out
A content script shares the page’s window, so window.onerror also fires
for the host site’s own errors. Only events with a frame inside your bundle
are reported. Their stack traces never reach your server.
npm install crxtraceimport * as CrxTrace from "crxtrace";
CrxTrace.init({ dsn: "https://your-ingest-endpoint/pk_live_..." });
// Name the work, so a worker death can be blamed on itconst transcript = await CrxTrace.track("fetchTranscript", getTranscript(id));That’s the whole setup. The same init() call goes in your service worker,
your content scripts, and any extension page — the SDK detects which surface
it’s in and routes accordingly.
The SDK is MIT licensed and complete. No feature flags, no phoning home, no crippled self-hosted mode.
The dsn is just an HTTPS endpoint that receives a JSON envelope and answers
2xx. That format is documented as a contract, and a
working receiver ships in the repo. Point it at your own backend and it will
not break under you.