Skip to content

Support and assign debugIds from web worker in main thread #16974

Closed
3 of 3 issues completed
getsentry/sentry-docs
#14395

Description

@Lms24

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.

Image

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:

  • registerWorker reads the debugIds from self and posts a message with them
  • webWorkerIntegration listens for the message and, once received, writes the debugId key-value pairs onto its global window._sentryDebugIds object.

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

  1. self-assigned this
    on Jul 14, 2025
  2. added theissue type on Jul 14, 2025
  3. timfish commented on Jul 14, 2025

    @timfish
    Collaborator

    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

  4. Lms24 commented on Jul 14, 2025

    @Lms24
    MemberAuthor

    yeah that's a good point, thanks for raising! I guess that we can patch worker.addEventListener for 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 called Sentry.webWorkerIntegration() or otherwise they risk messages bleeding through.

  5. Lms24 commented on Jul 14, 2025

    @Lms24
    MemberAuthor

    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 call event.stopImmediatePropagation() which should avoid all other later on registered handlers from being triggered 🤔

    I'll experiment with this for a bit and report back

    Update: Seems like this works as expected

  6. timfish commented on Jul 14, 2025

    @timfish
    Collaborator

    within our listener we can call event.stopImmediatePropagation()

    Oh nice, that works!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions