feat(android): [Unhandled Sessions 3] Add internal non-terminating envelope capture - #5918
Conversation
Hybrid runtimes such as Flutter report unhandled exceptions that do not terminate the process. Routing those through captureEnvelope ends the session as crashed and starts a replacement one, which understates crash-free session rates. The new entry point keeps the session alive with the same id, increments its error count, and marks it pending-unhandled so it finalizes as unhandled at its natural end. Co-authored-by: Cursor <cursoragent@cursor.com>
🚨 Detected changes in high risk code 🚨High-risk code has higher potential to break the SDK and may be hard to test. To prevent severe bugs, apply the rollout process for releasing such changes and be extra careful when changing and reviewing these files:
|
1 similar comment
🚨 Detected changes in high risk code 🚨High-risk code has higher potential to break the SDK and may be hard to test. To prevent severe bugs, apply the rollout process for releasing such changes and be extra careful when changing and reviewing these files:
|
Instructions and example for changelogPlease add an entry to Example: ## Unreleased
### Features
- [Unhandled Sessions 3] Add internal non-terminating envelope capture ([#5918](https://github.com/getsentry/sentry-java/pull/5918))If none of the above apply, you can opt out of this check by adding |
📲 Install BuildsAndroid
|
…ions-internal-api
Co-authored-by: Cursor <cursoragent@cursor.com>
🚨 Detected changes in high risk code 🚨High-risk code has higher potential to break the SDK and may be hard to test. To prevent severe bugs, apply the rollout process for releasing such changes and be extra careful when changing and reviewing these files:
|
Performance metrics 🚀
|
|
Not actually merged into The collection branch has been restored to its empty commit and the stack rebuilt unchanged. This PR continues as #5921. |
PR Stack (Unhandled Sessions)
📜 Description
Adds
InternalSentrySdk.captureEnvelopeNonTerminating(byte[])for hybrid runtimes where an unhandled exception does not terminate the process.Compared to
captureEnvelope(byte[], boolean), the new path does not treathandled=falseas a crash. Instead it:Okwith the same session id on the scope,The session is finalized later by normal lifecycle (
endSession, background, or previous-session recovery) asunhandled, unless a native crash escalates it tocrashed.AbnormalandCrashedremain authoritative, andcaptureEnvelope(byte[], boolean)is unchanged for the SDKs already using it.Also in this PR:
Scope.IWithSessionis widened to public soInternalSentrySdkcan mutate the session under the scope lock. This is its only consumer, hence it lives here rather than in PR 1.readEnvelopehelper for both capture paths, andcatch (Throwable)narrowed tocatch (Exception)soOutOfMemoryErrorand friends propagate instead of being swallowed. NoteIEnvelopeReader.readdeclares onlyIOExceptionbut also throws uncheckedIllegalArgumentExceptionfor malformed payloads, which is why the helper still catchesException.💡 Motivation and Context
Flutter currently forwards
handled=falseevents through the terminating hybrid capture path. That marks the sessioncrashedand may start a replacement session even though the Flutter process keeps running, incorrectly lowering crash-free session rates.This follows the session protocol's
unhandledstatus for exceptions that are unhandled by the application but do not terminate the process.💚 How did you test it?
New
InternalSentrySdkTestcoverage for: the session stayingOkwith the same id and the marker persisted to disk;endSessionafterwards finalizing asunhandled; and a subsequent hard crash through the existing terminating API finalizing the old session ascrashed(clearing the marker) and starting a freshOksession. ExistingcaptureEnvelopetests confirm the terminating path is unchanged, including malformed-payload handling.📝 Checklist
sendDefaultPIIis enabled.🔮 Next steps
Update the Flutter Android bridge to use
captureEnvelopeNonTerminatingfor non-terminating unhandled events.