Skip to content

feat(android): [Unhandled Sessions 3] Add internal non-terminating envelope capture - #5918

Merged
buenaflor merged 4 commits into
feat/unhandled-sessionsfrom
feat/unhandled-sessions-internal-api
Aug 10, 2026
Merged

feat(android): [Unhandled Sessions 3] Add internal non-terminating envelope capture#5918
buenaflor merged 4 commits into
feat/unhandled-sessionsfrom
feat/unhandled-sessions-internal-api

Conversation

@buenaflor

@buenaflor buenaflor commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

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 treat handled=false as a crash. Instead it:

  • marks the current session pending-unhandled and increments its error count,
  • keeps the session Ok with the same session id on the scope,
  • attaches no session item to the envelope and starts no new session,
  • persists the session so the marker survives process death.

The session is finalized later by normal lifecycle (endSession, background, or previous-session recovery) as unhandled, unless a native crash escalates it to crashed. Abnormal and Crashed remain authoritative, and captureEnvelope(byte[], boolean) is unchanged for the SDKs already using it.

Also in this PR:

  • Scope.IWithSession is widened to public so InternalSentrySdk can mutate the session under the scope lock. This is its only consumer, hence it lives here rather than in PR 1.
  • A shared private readEnvelope helper for both capture paths, and catch (Throwable) narrowed to catch (Exception) so OutOfMemoryError and friends propagate instead of being swallowed. Note IEnvelopeReader.read declares only IOException but also throws unchecked IllegalArgumentException for malformed payloads, which is why the helper still catches Exception.

💡 Motivation and Context

Flutter currently forwards handled=false events through the terminating hybrid capture path. That marks the session crashed and may start a replacement session even though the Flutter process keeps running, incorrectly lowering crash-free session rates.

This follows the session protocol's unhandled status for exceptions that are unhandled by the application but do not terminate the process.

💚 How did you test it?

New InternalSentrySdkTest coverage for: the session staying Ok with the same id and the marker persisted to disk; endSession afterwards finalizing as unhandled; and a subsequent hard crash through the existing terminating API finalizing the old session as crashed (clearing the marker) and starting a fresh Ok session. Existing captureEnvelope tests confirm the terminating path is unchanged, including malformed-payload handling.

📝 Checklist

  • I added GH Issue ID & Linear ID
  • I added tests to verify the changes.
  • No new PII added or SDK only sends newly added PII if sendDefaultPII is enabled.
  • I updated the docs if needed.
  • I updated the wizard if needed.
  • Review from the native team if needed.
  • No breaking change or entry added to the changelog.
  • No breaking change for hybrid SDKs or communicated to hybrid SDKs.
  • Public API changes reviewed by another Mobile SDK team member or implemented according to the develop docs spec.

🔮 Next steps

Update the Flutter Android bridge to use captureEnvelopeNonTerminating for non-terminating unhandled events.

⚠️ Merge this PR using a merge commit (not squash). Only the collection branch is squash-merged into main.

buenaflor and others added 2 commits August 10, 2026 11:26
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>
Co-authored-by: Cursor <cursoragent@cursor.com>
@github-actions

Copy link
Copy Markdown
Contributor

🚨 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:

  • sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java

1 similar comment
@github-actions

Copy link
Copy Markdown
Contributor

🚨 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:

  • sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java

@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor
Fails
🚫 Please consider adding a changelog entry for the next release.
Messages
📖 Do not forget to update Sentry-docs with your feature once the pull request gets approved.

Instructions and example for changelog

Please add an entry to CHANGELOG.md to the "Unreleased" section. Make sure the entry includes this PR's number.

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 #skip-changelog to the PR description or adding a skip-changelog label.

Generated by 🚫 dangerJS against e1b73c5

@sentry

sentry Bot commented Aug 10, 2026

Copy link
Copy Markdown

📲 Install Builds

Android

🔗 App Name App ID Version Configuration
SDK Size io.sentry.tests.size 8.52.0 (1) release

⚙️ sentry-android Build Distribution Settings

Base automatically changed from feat/unhandled-sessions-cache to feat/unhandled-sessions August 10, 2026 09:40
@buenaflor
buenaflor merged commit e1b73c5 into feat/unhandled-sessions Aug 10, 2026
19 of 21 checks passed
@buenaflor
buenaflor deleted the feat/unhandled-sessions-internal-api branch August 10, 2026 09:40
@github-actions

Copy link
Copy Markdown
Contributor

🚨 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:

  • sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java

@buenaflor
buenaflor restored the feat/unhandled-sessions-internal-api branch August 10, 2026 09:48
@github-actions

Copy link
Copy Markdown
Contributor

Performance metrics 🚀

  Plain With Sentry Diff
Startup time 334.13 ms 400.48 ms 66.35 ms
Size 0 B 0 B 0 B

@buenaflor

Copy link
Copy Markdown
Contributor Author

Not actually merged into main. This PR was auto-closed when the collection branch feat/unhandled-sessions was accidentally fast-forwarded to the tip of the stack, which made GitHub consider every stack branch merged into its base.

The collection branch has been restored to its empty commit and the stack rebuilt unchanged. This PR continues as #5921.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant