Skip to content

Sentry in a Chrome Extension Service Worker (Manifest V3): What Works

6 min readModerok team

Sentry in a Manifest V3 Chrome extension: what Sentry's own docs prescribe, what they never mention, and what error logs you get without it.

Yes, you can send errors from a Manifest V3 Chrome extension to Sentry, and Sentry documents how, just not on the quickstart page. Its guidance for browser extensions says you "should not use Sentry.init(), as this will pollute the global state", and tells you to build a BrowserClient and Scope by hand with six default integrations filtered out (Sentry, as of October 2026). Two of them are GlobalHandlers and Breadcrumbs, so automatic exception capture is what you trade away.

TL;DR: Install with npm, because the "Loader Script" Sentry also documents (install, as of October 2026) would be remotely hosted code, which Chrome bans (developer.chrome.com, as of October 2026). Then follow the shared-environments recipe: a manual BrowserClient, no GlobalHandlers, and a scope.captureException() call wherever you want an error recorded (Sentry, as of October 2026). If you would rather not assemble a client, uncaught exceptions and unhandled rejections arrive as __error events by default in the Moderok SDK. More in the analytics tools roundup.

The 30-second comparison

Every Sentry cell below is what that page said when we opened it on 2 October 2026.

QuestionSentry (JavaScript browser SDK)Moderok
What the docs prescribe inside an extension"you should not use Sentry.init()", build a BrowserClient and Scope manually, as of October 2026 (shared environments)Three parts: npm install @moderok/sdk, "permissions": ["storage"], then Moderok.init({ appKey: "mk_..." }) at the top level of the background service worker (getting started)
Do the docs name the MV3 background service worker?Its browser-extension page contains neither "service worker" nor "Manifest V3", as of October 2026 (shared environments)Prerequisite is "A Manifest V3 extension (Chromium or Firefox 109+)" (getting started)
Documented install methods"NPM" and "Loader Script", as of October 2026 (install)npm, or a copied dist/moderok.min.js (install)
What it captures without your codeThe example ends in scope.captureException(new Error("example")), with GlobalHandlers filtered out, as of October 2026 (shared environments)Uncaught exceptions and unhandled rejections become __error, trackErrors defaulting to true (configuration)
Why events go missing after setup"an Inbound Filter might be active", so disable "filtering out errors known to be caused by browser extensions", as of October 2026 (shared environments)Capture is capped at "20 captured errors per minute per JS context" (configuration); the SDK "deduplicates bursts" (debugging)

Round 1What Sentry's docs prescribe for a Manifest V3 Chrome extension

One page covers your case, and it is not a Chrome-specific one. Sentry's shared-environments page lists where it applies, starting with "Browser Extensions", then "VSCode Extensions, Third-Party Widgets, Plugin Architecture, Libraries" (Sentry, as of October 2026).

The recipe is explicit: no Sentry.init(), construct a BrowserClient with makeFetchTransport and defaultStackParser, attach it to new Scope(), and filter six integrations by name, BrowserApiErrors, BrowserSession, Breadcrumbs, ConversationId, GlobalHandlers and FunctionToString (Sentry, as of October 2026).

Now the gap. Opened today, that page contains neither the phrase "service worker" nor "Manifest V3". The other page people land on is scoped to a different API: "Sentry's Browser SDK supports the Web Workers API", with a recommended example that constructs the worker itself, const worker = new Worker("worker.js"); (Sentry, as of October 2026). An MV3 background service worker is not that, nothing in your extension calls new Worker() to create it, and that page does not mention service workers either.

What it does offer is a "Manually Capturing Errors" section: "you can also import the SDK in the worker and initialize it", which "completely decouples" the worker SDK from any main-thread instance (Sentry, as of October 2026). That is the shape extension code ends up in, derived from a page about a different worker type.

Two Chrome rules narrow this further. "In Manifest V3, all of your extension's logic must be part of the extension package. You can no longer load and execute remotely hosted files" (developer.chrome.com, as of October 2026), so the Loader Script is out. And "Like its web counterpart, an extension service worker cannot access the DOM, though you can use it if needed with offscreen documents" (developer.chrome.com, as of October 2026). An offscreen document is a separate context with its own lifetime, not a DOM for your worker: same missing global as document is not defined.

Winner

Moderok, on documentation that names the context you ship into: the stated prerequisite is "A Manifest V3 extension (Chromium or Firefox 109+)", with init() at the top level of the background service worker (getting started).

Round 2The two places Sentry events quietly vanish

Sentry's shared-environments page names both.

The first is global state. If your extension calls Sentry.init() and a website also uses Sentry, "the extension may send events to the website's Sentry project, or vice versa" (Sentry, as of October 2026). There is an escape hatch, skipBrowserExtensionCheck, "Available in all browser-based SDKs since version 8.37.0", and the same page tells you not to reach for it: "You shouldn't use this option if you're in fact using the SDK in a browser extension or another shared environment. Initializing the SDK via Sentry.init has no advantages over manually setting up the client and scope as described on this page" (Sentry, as of October 2026).

The second is server side, and it burns an afternoon. If nothing arrives, "an Inbound Filter might be active", and the fix is Project Settings, stop "filtering out errors known to be caused by browser extensions" (Sentry, as of October 2026). That filter "checks the error message and event source to see if it's a known error coming from a browser extension" (Sentry, as of October 2026). Your extension is, of course, a browser extension.

Winner

Sentry, for documenting its own sharp edges precisely enough to fix in one sitting.

Round 3What arrives without you writing a capture call

Dropping GlobalHandlers is the point: those handlers are the global state you were told to avoid. So capture becomes something you write, in every catch and everywhere a promise can reject, which is why the documented example ends in an explicit scope.captureException(new Error("example")) (Sentry, as of October 2026).

The Moderok SDK goes the other way. trackErrors defaults to true, recording uncaught exceptions and unhandled rejections as __error (configuration), alongside __install, __update, __first_open and __daily_ping (automatic events). That coverage is per context: "Each context (background, popup, options, content script) has its own SDK instance" (initialization), so init() goes in each one you want covered.

Two things stay manual, both documented: console.error mirroring is off unless you set captureConsoleErrors: true, and chrome.runtime.lastError needs captureLastError(apiName, lastError, props?) in the callback (error tracking). That second one is no SDK's fault: callback-style failures are never thrown, the problem in catching chrome.runtime.lastError.

Winner

Moderok, for global handlers on by default in each context you initialize rather than discipline in every catch.

Round 4How much of one error report you get

Be honest about the trade. Six integrations go, and losing Breadcrumbs hurts. What you keep includes symbolication, which Sentry documents as a step you perform: "To ensure that errors from web workers are properly mapped to their original source code, you need to provide source maps to Sentry" (Sentry, as of October 2026).

There is no equivalent on the Moderok side: nothing in the SDK docs describes a source map or symbolication step (error tracking), so you read the stack your bundle produced. Size matters there too, since a single event's JSON is capped at "8 KiB" and oversized events are skipped (configuration).

Winner

Sentry, on report depth, even after the extension recipe sheds integrations.

When Sentry is the right choice

Pick Sentry if you already run it elsewhere and want extension crashes in the same project as the rest of your stack, or if you need stack traces mapped back to original source, which it documents (Sentry, as of October 2026). Its extension guidance is documentation rather than folklore, specific about what to filter and why, and the Developer plan ("Free", "5k errors", "30-day lookback", "One user", as of October 2026, pricing) is enough to find out whether your worker is throwing at all.

What an error monitor will not tell you is how many people use the extension and whether they come back. If that is your question, and you want crash visibility included rather than configured, start with the error tracking guide or the product page.