Skip to content

Commit 6ba3cd5

Browse files
committed
feat(browser)!: Send session status unhandled instead of crashed for unhandled errors
1 parent 56c8ca3 commit 6ba3cd5

6 files changed

Lines changed: 70 additions & 5 deletions

File tree

dev-packages/browser-integration-tests/suites/sessions/page-lifecycle/test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ sentryTest('Updates the session when an error is thrown', async ({ getLocalTestU
9999
...initialSession,
100100
errors: 1,
101101
init: false,
102-
status: 'crashed',
102+
status: 'unhandled',
103103
timestamp: expect.any(String),
104104
});
105105
});

dev-packages/browser-integration-tests/suites/sessions/update-session/test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ sentryTest('should update session when an error is thrown.', async ({ getLocalTe
1919

2020
expect(updatedSession.init).toBe(false);
2121
expect(updatedSession.errors).toBe(1);
22-
expect(updatedSession.status).toBe('crashed');
22+
expect(updatedSession.status).toBe('unhandled');
2323
expect(pageloadSession.sid).toBe(updatedSession.sid);
2424
});
2525

packages/browser/src/client.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type {
77
Options as CoreOptions,
88
ParameterizedString,
99
Scope,
10+
SessionStatus,
1011
SeverityLevel,
1112
} from '@sentry/core/browser';
1213
import { addAutoIpAddressToSession, applySdkMetadata, Client, getSDKSource } from '@sentry/core/browser';
@@ -157,6 +158,14 @@ export class BrowserClient extends Client<BrowserClientOptions> {
157158

158159
return super._prepareEvent(event, hint, currentScope, isolationScope);
159160
}
161+
162+
/**
163+
* @inheritDoc
164+
*/
165+
protected _getUnhandledSessionStatus(): SessionStatus {
166+
// Unhandled errors don't actually crash the browser, so we report `unhandled` rather than `crashed`.
167+
return 'unhandled';
168+
}
160169
}
161170

162171
/** Exported only for tests. */

packages/browser/test/client.test.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
* @vitest-environment jsdom
33
*/
44

5+
import { getCurrentScope, makeSession, setCurrentClient } from '@sentry/core/browser';
56
import { afterEach, describe, expect, it, vi } from 'vitest';
67
import { applyDefaultOptions, BrowserClient } from '../src/client';
78
import { WINDOW } from '../src/helpers';
@@ -49,6 +50,51 @@ describe('BrowserClient', () => {
4950
expect(flushOutcomesSpy).not.toHaveBeenCalled();
5051
expect(flushSpy).toHaveBeenCalledTimes(1);
5152
});
53+
54+
describe('session status on unhandled errors', () => {
55+
afterEach(() => {
56+
getCurrentScope().setSession(undefined);
57+
getCurrentScope().setClient(undefined);
58+
});
59+
60+
it('sets the session status to "unhandled" for an unhandled exception', () => {
61+
client = new BrowserClient(getDefaultBrowserClientOptions());
62+
setCurrentClient(client);
63+
64+
const session = makeSession();
65+
getCurrentScope().setSession(session);
66+
67+
client.captureException(new Error('test'), { mechanism: { handled: false } });
68+
69+
expect(session.status).toBe('unhandled');
70+
expect(session.errors).toBe(1);
71+
});
72+
73+
it('sets the session status to "unhandled" for a fatal event', () => {
74+
client = new BrowserClient(getDefaultBrowserClientOptions());
75+
setCurrentClient(client);
76+
77+
const session = makeSession();
78+
getCurrentScope().setSession(session);
79+
80+
client.captureEvent({ message: 'test', level: 'fatal' });
81+
82+
expect(session.status).toBe('unhandled');
83+
});
84+
85+
it('keeps the session status "ok" for a handled exception', () => {
86+
client = new BrowserClient(getDefaultBrowserClientOptions());
87+
setCurrentClient(client);
88+
89+
const session = makeSession();
90+
getCurrentScope().setSession(session);
91+
92+
client.captureException(new Error('test'));
93+
94+
expect(session.status).toBe('ok');
95+
expect(session.errors).toBe(1);
96+
});
97+
});
5298
});
5399

54100
describe('applyDefaultOptions', () => {

packages/core/src/client.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import type { ParameterizedString } from './types/parameterize';
3232
import type { ReplayEndEvent, ReplayStartEvent } from './types/replay';
3333
import type { RequestEventData } from './types/request';
3434
import type { SdkMetadata } from './types/sdkmetadata';
35-
import type { Session, SessionAggregates } from './types/session';
35+
import type { Session, SessionAggregates, SessionStatus } from './types/session';
3636
import type { SeverityLevel } from './types/severity';
3737
import type { Span, SpanAttributes, SpanContextData, SpanJSON, StreamedSpanJSON } from './types/span';
3838
import type { StartSpanOptions } from './types/startSpanOptions';
@@ -1280,13 +1280,23 @@ export abstract class Client<O extends ClientOptions = ClientOptions> {
12801280

12811281
if (shouldUpdateAndSend) {
12821282
updateSession(session, {
1283-
...(crashed && { status: 'crashed' }),
1283+
...(crashed && { status: this._getUnhandledSessionStatus() }),
12841284
errors: session.errors || Number(errored || crashed),
12851285
});
12861286
this.captureSession(session);
12871287
}
12881288
}
12891289

1290+
/**
1291+
* The session status to set when an unhandled error terminates a session.
1292+
*
1293+
* Defaults to `'crashed'`. Browser SDKs override this to `'unhandled'` because unhandled errors
1294+
* don't actually crash the browser.
1295+
*/
1296+
protected _getUnhandledSessionStatus(): SessionStatus {
1297+
return 'crashed';
1298+
}
1299+
12901300
/**
12911301
* Determine if the client is finished processing. Returns a promise because it will wait `timeout` ms before saying
12921302
* "no" (resolving to `false`) in order to give the client a chance to potentially finish first.

packages/core/src/types/session.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export interface Session {
3030

3131
export type SessionContext = Partial<Session>;
3232

33-
export type SessionStatus = 'ok' | 'exited' | 'crashed' | 'abnormal';
33+
export type SessionStatus = 'ok' | 'exited' | 'crashed' | 'abnormal' | 'unhandled';
3434

3535
/** JSDoc */
3636
export interface SessionAggregates {

0 commit comments

Comments
 (0)