Description
Flutter unhandled errors (handled: false) do not terminate the process, but native SDKs currently end the session as crashed and often start a new one. That skews crash-free session metrics.
A new session status type called unhandled has been added that can correctly represent our use case.
Relay already accepts terminal status: unhandled (relay#4939) to distinguish non-terminating unhandled errors from real crashes.
Reference behavior (Unity / .NET)
getsentry/sentry-dotnet#4633
On capture of a non-terminal unhandled error: do not send a terminal session status. Keep the same session (status: ok), set a sticky pending unhandled flag (persisted locally only). Do not end the session or start a new one.
On graceful session end (quit / background end / next-launch recovery with no crash): finalize as status: unhandled.
If a real crash happens later (crashedLastRun / terminal crash): finalize as status: crashed (crashed wins over pending).
unhandled is only sent when the session actually ends. Setting it on capture would be terminal and would block upgrading the same sid to crashed.
sentry-java (Android)
Today InternalSentrySdk.captureEnvelope treats hybrid handled=false as crash: set Crashed, end session, optionally startSession() (Flutter passes containsUnhandledException as maybeStartNewSession).
Calling captureEnvelope from Flutter implies the process is still alive → treat as non-terminal. No separate isTerminating flag needed.
Needed: Session.State.Unhandled + pendingUnhandled; change hybrid captureEnvelope to mark pending only; finalize/escalate on session end / crash recovery (Unity-style).
sentry-cocoa (iOS/macOS)
Same bug, different entry point. Flutter calls PrivateSentrySDKOnly.capture(envelope); Cocoa’s SentryHub.captureEnvelope → updateSessionState(..., startNewSession:YES) ends unhandled envelopes as crashed and starts a new session. Flutter’s containsUnhandledException arg is unused; Cocoa inspects the envelope itself.
SentrySessionStatus has no unhandled yet.
Needed: same Unity-style pending → finalize/escalate behavior in hub session update + session model. Real crash path (storeEnvelope / crash integration) stays crashed.
Description
Flutter unhandled errors (handled: false) do not terminate the process, but native SDKs currently end the session as crashed and often start a new one. That skews crash-free session metrics.
A new session status type called
unhandledhas been added that can correctly represent our use case.Relay already accepts terminal status: unhandled (relay#4939) to distinguish non-terminating unhandled errors from real crashes.
Reference behavior (Unity / .NET)
getsentry/sentry-dotnet#4633
On capture of a non-terminal unhandled error: do not send a terminal session status. Keep the same session (status: ok), set a sticky pending unhandled flag (persisted locally only). Do not end the session or start a new one.
On graceful session end (quit / background end / next-launch recovery with no crash): finalize as status: unhandled.
If a real crash happens later (crashedLastRun / terminal crash): finalize as status: crashed (crashed wins over pending).
unhandled is only sent when the session actually ends. Setting it on capture would be terminal and would block upgrading the same sid to crashed.
sentry-java (Android)
Today InternalSentrySdk.captureEnvelope treats hybrid handled=false as crash: set Crashed, end session, optionally startSession() (Flutter passes containsUnhandledException as maybeStartNewSession).
Calling captureEnvelope from Flutter implies the process is still alive → treat as non-terminal. No separate isTerminating flag needed.
Needed: Session.State.Unhandled + pendingUnhandled; change hybrid captureEnvelope to mark pending only; finalize/escalate on session end / crash recovery (Unity-style).
sentry-cocoa (iOS/macOS)
Same bug, different entry point. Flutter calls PrivateSentrySDKOnly.capture(envelope); Cocoa’s SentryHub.captureEnvelope → updateSessionState(..., startNewSession:YES) ends unhandled envelopes as crashed and starts a new session. Flutter’s containsUnhandledException arg is unused; Cocoa inspects the envelope itself.
SentrySessionStatus has no unhandled yet.
Needed: same Unity-style pending → finalize/escalate behavior in hub session update + session model. Real crash path (storeEnvelope / crash integration) stays crashed.