Repository navigation
Description
Problem Statement
Today, you can initialize the Sentry SDK in the main thread and it will capture errors thrown in a web worker (at least in regular workers; not sure about service workers). If users add source map upload via debug ids however, it's not easily possible right now, to correctly symbolicate the errors from the worker.
This is because, the global object in the worker self contains the debugId for its file(s) and the main thread only contains the debugIds of its files on window. Since there's no shared global object, there's no place where all of these debugIds are present. When we assign the debugIds to the error event, we can only access the main thread's global and therefore, the SDK has no knowledge of the debugIds of the worker's file(s).
There is a (sub-optimal) solution to "fix" this today: Init the SDK another time in the worker. This will make the SDK in the worker capture the error, look up the correct debugId and send it to Sentry. There are (at least) two significant drawbacks though:
- In my testing, the error still bubbled up to the main thread's global handler and Sentry captured it twice (apparently our dedupe logic doesn't work for this case)
- obviously, all worker errors lose the trace context, replay connection, etc of the main thread
Solution Brainstorm
Well if we don't want to double init the SDK, we need to let the main thread know about the debugIds of the worker.
This could look as follows:
// main.js
Sentry.init();
const worker = new MyWorker();
Sentry.addIntegration(Sentry.webWorkerIntegration(worker))// myWorker.js
Sentry.registerWorker(self)Internally, both APIs communicate over the worker's message channel:
registerWorkerreads the debugIds fromselfand posts a message with themwebWorkerIntegrationlistens for the message and, once received, writes the debugId key-value pairs onto its globalwindow._sentryDebugIdsobject.
Now, once the worker throws an error that's caught by the SDK, it will be able to pick up the debugId for the stack frames from the worker and attach them --> event in Sentry ends up symbolicated.#
Besides symbolicated events, this now also has the advantage that the worker errors will get the same traceId as the main thread, as well as replayId connections being made. So all in all a better experience.
I have a PoC of this working -- the new SDK APIs would probably look very similar.
Activity
webWorkerIntegration#16975Sentry.registerWorker(self)API #16976timfish commentedon Jul 14, 2025
The main issue I see is that users might be using the message channel themselves. That means we need to handle (and ignore) their messages and they will need to ignore our message.
In the Electron SDK we are able to wrap the utility process forking so that we can hide these messages from users:
https://github.com/getsentry/sentry-electron/blob/f7d24dd2b96f670367821ec9f0bffac2fc95fc63/src/main/utility-processes.ts#L31
https://github.com/getsentry/sentry-electron/blob/f7d24dd2b96f670367821ec9f0bffac2fc95fc63/src/utility/transport.ts#L42
Lms24 commentedon Jul 14, 2025
yeah that's a good point, thanks for raising! I guess that we can patch
worker.addEventListenerfor users to not trigger for sentry-emitted messages. Similarly to what you linked to in the electron case. Though this will require users to ensure that they only add their listeners after they calledSentry.webWorkerIntegration()or otherwise they risk messages bleeding through.Lms24 commentedon Jul 14, 2025
Though I wonder, if we have this requirement anyway that users need to first call
Sentry.webWorkerIntegration()(for the patch to apply correctly): Do we need the patch at all? Cause within our listener we can callevent.stopImmediatePropagation()which should avoid all other later on registered handlers from being triggered 🤔I'll experiment with this for a bit and report backUpdate: Seems like this works as expected
timfish commentedon Jul 14, 2025
Oh nice, that works!
feat(js): Update web worker guide with new `webWorkerIntegration` APIs (
feat(js): Update web worker guide with new `webWorkerIntegration` APIs (