10BC0 fix(browser): Capture unhandled rejection errors for web worker integration by RulaKhaled · Pull Request #18054 · getsentry/sentry-javascript · GitHub
[go: up one dir, main page]

Skip to content

Conversation

@RulaKhaled
Copy link
Member

Previously, only synchronous errors thrown in web workers would bubble up and be captured. Unhandled promise rejections within workers would fail silently since they don't propagate to the parent thread automatically. This enhancement ensures complete error coverage for web worker code.

@linear
Copy link
linear bot commented Oct 29, 2025

@RulaKhaled RulaKhaled marked this pull request as ready for review October 30, 2025 09:45
@RulaKhaled RulaKhaled requested a review from Lms24 October 30, 2025 09:45
Comment on lines +231 to +241
// The parent will handle primitives vs errors the same way globalHandlers does
const serializedError: SerializedWorkerError = {
reason: reason,
filename: self.location?.href,
};

// Forward to parent thread
self.postMessage({
_sentryMessage: true,
_sentryWorkerError: serializedError,
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Structured cloning of Error objects from web workers via postMessage causes loss of prototype, preventing proper stack trace parsing.
Severity: HIGH | Confidence: 0.95

🔍 Detailed Analysis

When an Error object is sent from a web worker to the main thread via postMessage, JavaScript's structured cloning converts it into a plain object, losing its Error prototype. This occurs for unhandled promise rejections in web workers. The eventFromUnknownInput() function treats the cloned object as a plain object, as isError() fails. Consequently, its stack property is not parsed into structured StackFrame objects but is serialized into the __serialized__ extra field, preventing proper stack trace display.

💡 Suggested Fix

Either serialize the stack property separately and pass it as a syntheticException, or serialize the stack frames directly from the worker before sending the message.

🤖 Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: packages/browser/src/integrations/webWorker.ts#L227-L241

Potential issue: When an `Error` object is sent from a web worker to the main thread via
`postMessage`, JavaScript's structured cloning converts it into a plain object, losing
its `Error` prototype. This occurs for unhandled promise rejections in web workers. The
`eventFromUnknownInput()` function treats the cloned object as a plain object, as
`isError()` fails. Consequently, its `stack` property is not parsed into structured
`StackFrame` objects but is serialized into the `__serialized__` extra field, preventing
proper stack trace display.

Did we get this right? 👍 / 👎 to inform future reviews.

Comment on lines +140 to +151
const stackParser = client.getOptions().stackParser;
const attachStacktrace = client.getOptions().attachStacktrace;

const error = workerError.reason;

// Follow same pattern as globalHandlers for unhandledrejection
// Handle both primitives and errors the same way
const event = isPrimitive(error)
? _eventFromRejectionWithPrimitive(error)
: eventFromUnknownInput(stackParser, error, undefined, attachStacktrace, true);

event.level = 'error';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

m: Is there a reason why we need to manually construct the event? can we call captureException(workerError.reason) instead? I might be missing something, so just asking.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is how we handle errors in the global handler, i just want to preserve the same behavior as other promise rejections events.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point! I missed this and you're right, we should handle this in the same way.

// The parent will handle primitives vs errors the same way globalHandlers does
const serializedError: SerializedWorkerError = {
reason: reason,
filename: self.location?.href,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good idea!

@RulaKhaled RulaKhaled merged commit 600e27a into develop Nov 3, 2025
287 of 290 checks passed
@RulaKhaled RulaKhaled deleted the rolaabuhasna/js-1097-webworkerintegration-doesnt-handle-unhandledrejection-events branch November 3, 2025 16:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

0