diff --git a/packages/browser/src/eventbuilder.ts b/packages/browser/src/eventbuilder.ts index 76e38ec00468..cb489980527f 100644 --- a/packages/browser/src/eventbuilder.ts +++ b/packages/browser/src/eventbuilder.ts @@ -290,6 +290,15 @@ export function eventFromUnknownInput( if ('stack' in (exception as Error)) { event = eventFromError(stackParser, exception as Error); + + const firstException = event.exception?.values?.[0]; + if (attachStacktrace && syntheticException && firstException && !firstException.stacktrace) { + const frames = parseStackFrames(stackParser, syntheticException); + if (frames.length) { + firstException.stacktrace = { frames }; + addExceptionMechanism(event, { synthetic: true }); + } + } } else { const name = domException.name || (isDOMError(domException) ? 'DOMError' : 'DOMException'); const message = domException.message ? `${name}: ${domException.message}` : name; diff --git a/packages/browser/test/eventbuilder.test.ts b/packages/browser/test/eventbuilder.test.ts index bc99c9c95e49..7b5df4fb1c19 100644 --- a/packages/browser/test/eventbuilder.test.ts +++ b/packages/browser/test/eventbuilder.test.ts @@ -168,6 +168,91 @@ describe('eventFromUnknownInput', () => { }, }); }); + + it('add a synthetic stack trace to DOMException with empty stack traces if attachStacktrace is true', async () => { + const exception = new DOMException('The string did not match the expected pattern.', 'SyntaxError'); + exception.stack = ''; + + const syntheticException = new Error('Test message'); + const event = await eventFromUnknownInput(defaultStackParser, exception, syntheticException, true); + expect(event.exception?.values?.[0]).toEqual( + expect.objectContaining({ + mechanism: { handled: true, synthetic: true, type: 'generic' }, + stacktrace: { + frames: expect.arrayContaining([expect.any(Object), expect.any(Object)]), + }, + type: 'SyntaxError', + value: 'The string did not match the expected pattern.', + }), + ); + }); + + it('preserves DOMException type when stack is an empty string', () => { + const exception = new DOMException('The string did not match the expected pattern.', 'SyntaxError'); + exception.stack = ''; + + const event = eventFromUnknownInput(defaultStackParser, exception, new Error('synthetic'), true); + + expect(event.exception?.values?.[0]).toMatchObject({ + type: 'SyntaxError', + value: 'The string did not match the expected pattern.', + }); + }); + + it('add a synthetic stack trace to DOMException without a stack traces property if attachStacktrace is true', async () => { + const exception = new DOMException('The string did not match the expected pattern.', 'SyntaxError'); + delete exception.stack; + + const syntheticException = new Error('Test message'); + const event = await eventFromUnknownInput(defaultStackParser, exception, syntheticException, true); + expect(event.exception?.values?.[0]).toEqual( + expect.objectContaining({ + mechanism: { handled: true, synthetic: true, type: 'generic' }, + stacktrace: { + frames: expect.arrayContaining([expect.any(Object), expect.any(Object)]), + }, + type: 'Error', + value: 'SyntaxError: The string did not match the expected pattern.', + }), + ); + }); + + it("doesn't add a synthetic stack trace to DOMException with empty stack traces if attachStacktrace is false", async () => { + const exception = new DOMException('The string did not match the expected pattern.', 'SyntaxError'); + exception.stack = ''; + + const syntheticException = new Error('Test message'); + const event = await eventFromUnknownInput(defaultStackParser, exception, syntheticException, false); + expect(event.exception?.values?.[0]).toEqual({ + type: 'SyntaxError', + value: 'The string did not match the expected pattern.', + }); + }); + + it("doesn't add a synthetic stack trace to DOMException with stack traces if attachStacktrace is true", async () => { + const exception = new DOMException('The string did not match the expected pattern.', 'SyntaxError'); + exception.stack = 'SyntaxError\n at :1:2'; + + const syntheticException = new Error('Test message'); + const event = await eventFromUnknownInput(defaultStackParser, exception, syntheticException, true); + expect(event.exception?.values?.[0]).toEqual( + expect.objectContaining({ + stacktrace: { + frames: [ + { + colno: 2, + filename: '', + function: '?', + in_app: true, + lineno: 1, + }, + ], + }, + type: 'SyntaxError', + value: 'The string did not match the expected pattern.', + }), + ); + }); }); describe('extractMessage', () => {