From a0fd76a3fc230e91ff1370248dae5aaa296d1cf7 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Mon, 10 Nov 2025 13:40:05 +0100 Subject: [PATCH 1/2] fix(core): Only use exception mechanism when updating session status from events with exceptions --- packages/core/src/client.ts | 6 +- packages/core/test/lib/client.test.ts | 103 ++++++++++++++++++++++++++ 2 files changed, 107 insertions(+), 2 deletions(-) diff --git a/packages/core/src/client.ts b/packages/core/src/client.ts index c3ff126732f8..53e0328965a4 100644 --- a/packages/core/src/client.ts +++ b/packages/core/src/client.ts @@ -1037,16 +1037,18 @@ export abstract class Client { /** Updates existing session based on the provided event */ protected _updateSessionFromEvent(session: Session, event: Event): void { + // initially, set `crashed` based on the event level and update from exceptions if there are any later on let crashed = event.level === 'fatal'; let errored = false; const exceptions = event.exception?.values; if (exceptions) { errored = true; + // reset crashed to false if there are exceptions, to ensure `mechanism.handled` is respected. + crashed = false; for (const ex of exceptions) { - const mechanism = ex.mechanism; - if (mechanism?.handled === false) { + if (ex.mechanism?.handled === false) { crashed = true; break; } diff --git a/packages/core/test/lib/client.test.ts b/packages/core/test/lib/client.test.ts index db25793ccf7b..87efdc854ca7 100644 --- a/packages/core/test/lib/client.test.ts +++ b/packages/core/test/lib/client.test.ts @@ -8,6 +8,7 @@ import { makeSession, Scope, setCurrentClient, + SeverityLevel, SyncPromise, withMonitor, } from '../../src'; @@ -2308,6 +2309,108 @@ describe('Client', () => { }); }); + describe('_updateSessionFromEvent()', () => { + describe('event has no exceptions', () => { + it('sets status to crashed if level is fatal', () => { + const client = new TestClient(getDefaultTestClientOptions()); + const session = makeSession(); + getCurrentScope().setSession(session); + + client.captureEvent({ message: 'test', level: 'fatal' }); + + const updatedSession = client.session; + + expect(updatedSession).toMatchObject({ + duration: expect.any(Number), + errors: 1, + init: false, + sid: expect.any(String), + started: expect.any(Number), + status: 'crashed', + timestamp: expect.any(Number), + }); + }); + + it.each(['error', 'warning', 'log', 'info', 'debug'] as const)( + 'sets status to ok if level is %s', + (level: SeverityLevel) => { + const client = new TestClient(getDefaultTestClientOptions()); + const session = makeSession(); + getCurrentScope().setSession(session); + + client.captureEvent({ message: 'test', level }); + + const updatedSession = client.session; + + expect(updatedSession?.status).toEqual('ok'); + }, + ); + }); + + describe('event has exceptions', () => { + it.each(['fatal', 'error', 'warning', 'log', 'info', 'debug'] as const)( + 'sets status ok for handled exceptions and ignores event level %s', + (level: SeverityLevel) => { + const client = new TestClient(getDefaultTestClientOptions()); + const session = makeSession(); + getCurrentScope().setSession(session); + + client.captureException(new Error('test'), { captureContext: { level } }); + + const updatedSession = client.session; + + expect(updatedSession?.status).toEqual('ok'); + }, + ); + + it.each(['fatal', 'error', 'warning', 'log', 'info', 'debug'] as const)( + 'sets status crashed for unhandled exceptions and ignores event level %s', + (level: SeverityLevel) => { + const client = new TestClient(getDefaultTestClientOptions()); + const session = makeSession(); + getCurrentScope().setSession(session); + + client.captureException(new Error('test'), { captureContext: { level }, mechanism: { handled: false } }); + + const updatedSession = client.session; + + expect(updatedSession?.status).toEqual('crashed'); + }, + ); + + it('sets status crashed if at least one exception is unhandled', () => { + const client = new TestClient(getDefaultTestClientOptions()); + const session = makeSession(); + getCurrentScope().setSession(session); + + const event: Event = { + exception: { + values: [ + { + mechanism: { type: 'generic', handled: true }, + }, + { + mechanism: { type: 'generic', handled: false }, + }, + { + mechanism: { type: 'generic', handled: true }, + }, + ], + }, + }; + + client.captureEvent(event); + + const updatedSession = client.session; + + expect(updatedSession).toMatchObject({ + status: 'crashed', + errors: 1, // an event with multiple exceptions still counts as one error in the session + }); + }); + }); + }); + describe('recordDroppedEvent()/_clearOutcomes()', () => { test('records and returns outcomes', () => { const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); From 2a48624a397fba83c9253ab6263218bdf802cb27 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Mon, 10 Nov 2025 14:58:21 +0100 Subject: [PATCH 2/2] lint --- packages/core/test/lib/client.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/test/lib/client.test.ts b/packages/core/test/lib/client.test.ts index 87efdc854ca7..acb4197cf4cf 100644 --- a/packages/core/test/lib/client.test.ts +++ b/packages/core/test/lib/client.test.ts @@ -1,4 +1,5 @@ import { afterEach, beforeEach, describe, expect, it, test, vi } from 'vitest'; +import type { SeverityLevel } from '../../src'; import { addBreadcrumb, dsnToString, @@ -8,7 +9,6 @@ import { makeSession, Scope, setCurrentClient, - SeverityLevel, SyncPromise, withMonitor, } from '../../src';