Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ sentryTest('should update session when an error is thrown.', async ({ getLocalTe
expect(updatedSession).toBeDefined();
expect(updatedSession.init).toBe(false);
expect(updatedSession.errors).toBe(1);
expect(updatedSession.status).toBe('crashed');
expect(updatedSession.status).toBe('unhandled');
expect(pageloadSession.sid).toBe(updatedSession.sid);
});

Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -963,7 +963,7 @@ export abstract class Client<O extends ClientOptions = ClientOptions> {

if (shouldUpdateAndSend) {
updateSession(session, {
...(crashed && { status: 'crashed' }),
...(crashed && { status: 'unhandled' }),
errors: session.errors || Number(errored || crashed),
});
this.captureSession(session);
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/scope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export interface ScopeContext {
export interface SdkProcessingMetadata {
[key: string]: unknown;
requestSession?: {
status: 'ok' | 'errored' | 'crashed';
status: 'ok' | 'errored' | 'crashed' | 'unhandled';
};
normalizedRequest?: RequestEventData;
dynamicSamplingContext?: Partial<DynamicSamplingContext>;
Expand Down
14 changes: 10 additions & 4 deletions packages/core/src/server-runtime-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,10 +225,16 @@ function setCurrentRequestSessionErroredOrCrashed(eventHint?: EventHint): void {
const isHandledException = eventHint?.mechanism?.handled ?? true;
// A request session can go from "errored" -> "crashed" but not "crashed" -> "errored".
// Crashed (unhandled exception) is worse than errored (handled exception).
if (isHandledException && requestSession.status !== 'crashed') {
requestSession.status = 'errored';
} else if (!isHandledException) {
requestSession.status = 'crashed';
if (isHandledException) {
// If it's a handled exception, we can only downgrade from 'ok' to 'errored'.
// We should not downgrade from 'unhandled' or 'crashed'.
if (requestSession.status === 'ok') {
requestSession.status = 'errored';
}
} else {
// If it's an unhandled exception, we always set the status to 'unhandled'.
// 'unhandled' is the most severe status.
requestSession.status = 'unhandled';
Copy link

Choose a reason for hiding this comment

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

Bug: Session Status Update Logic Changed

The new logic for handled exceptions only updates session status from 'ok' to 'errored', changing previous behavior where 'abnormal' sessions would also transition. Additionally, unhandled exceptions now set the status to 'unhandled', making the existing comment referencing 'crashed' inaccurate.

Fix in Cursor Fix in Web

}
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/types-hoist/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export interface Session {

export type SessionContext = Partial<Session>;

export type SessionStatus = 'ok' | 'exited' | 'crashed' | 'abnormal';
export type SessionStatus = 'ok' | 'exited' | 'crashed' | 'abnormal' | 'unhandled';

/** JSDoc */
export interface SessionAggregates {
Comment on lines 30 to 36
Copy link

Choose a reason for hiding this comment

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

Potential bug: The new 'unhandled' session status is not handled by all parts of the system, leading to data corruption in session aggregation and inconsistent status reporting.
  • Description: The introduction of the 'unhandled' session status is incomplete. The session aggregation logic in packages/node-core/src/integrations/http/httpServerIntegration.ts contains a hardcoded mapping of statuses that was not updated to include 'unhandled'. When a session with this new status is processed, the logic attempts to access a non-existent key, which results in bucket['undefined'] = NaN. This corrupts the session aggregate data used for health monitoring. Additionally, packages/core/src/server-runtime-client.ts was not updated and still sets the session status to 'crashed' for unhandled exceptions, creating an inconsistency with other parts of the client that now use 'unhandled'.

  • Suggested fix: Update the session aggregation mapping in httpServerIntegration.ts to include the 'unhandled' status. Also, update server-runtime-client.ts and any other remaining locations, including tests, to use the 'unhandled' status instead of 'crashed' for consistency.
    severity: 0.75, confidence: 0.98

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

Copy link
Author

@JealousGx JealousGx Oct 2, 2025

Choose a reason for hiding this comment

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

@Lms24 , we dont need to update the test cases in httpServerIntegration.test.ts file right? I noticed crashed represents unhandled cases, so, it would be pointless to handle unhandled separately, if I got that right.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const INTEGRATION_NAME = 'Http.Server';

const clientToRequestSessionAggregatesMap = new Map<
Client,
{ [timestampRoundedToSeconds: string]: { exited: number; crashed: number; errored: number } }
{ [timestampRoundedToSeconds: string]: { exited: number; crashed: number; errored: number; unhandled: number } }
>();

// We keep track of emit functions we wrapped, to avoid double wrapping
Expand Down Expand Up @@ -277,8 +277,12 @@ export function recordRequestSession(
const dateBucketKey = roundedDate.toISOString();

const existingClientAggregate = clientToRequestSessionAggregatesMap.get(client);
const bucket = existingClientAggregate?.[dateBucketKey] || { exited: 0, crashed: 0, errored: 0 };
bucket[({ ok: 'exited', crashed: 'crashed', errored: 'errored' } as const)[requestSession.status]]++;
const bucket = existingClientAggregate?.[dateBucketKey] || { exited: 0, crashed: 0, errored: 0, unhandled: 0 };
bucket[
({ ok: 'exited', crashed: 'crashed', errored: 'errored', unhandled: 'unhandled' } as const)[
requestSession.status
]
]++;

if (existingClientAggregate) {
existingClientAggregate[dateBucketKey] = bucket;
Expand All @@ -297,7 +301,7 @@ export function recordRequestSession(
started: timestamp,
exited: value.exited,
errored: value.errored,
crashed: value.crashed,
crashed: (value.crashed || 0) + (value.unhandled || 0),
}),
);
client.sendSession({ aggregates: aggregatePayload });
Expand Down