Skip to content

feat(android): Add non-terminating envelope capture for Flutter#5795

Draft
buenaflor wants to merge 7 commits into
mainfrom
feat/pending-unhandled-sessions
Draft

feat(android): Add non-terminating envelope capture for Flutter#5795
buenaflor wants to merge 7 commits into
mainfrom
feat/pending-unhandled-sessions

Conversation

@buenaflor

@buenaflor buenaflor commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

📜 Description

Add InternalSentrySdk.captureEnvelopeNonTerminating for hybrid runtimes such as Flutter where an unhandled exception does not terminate the process.

The new path keeps the current session alive with the same SID, increments its error count, persists a pending-unhandled marker in the existing session cache, and finalizes it as unhandled only when the session naturally ends. Existing Abnormal and Crashed terminal states remain authoritative, and the existing captureEnvelope(byte[], boolean) behavior used by other SDKs is unchanged.

💡 Motivation and Context

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

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?

  • Added coverage for pending session mutation and serialization, natural/abnormal/crashed terminal precedence, previous-session recovery, SID-aware cache start/end handling, null SID handling, and terminating capture compatibility.

📝 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.

Made with Cursor

buenaflor and others added 5 commits July 16, 2026 13:53
…-unhandled sessions

Hybrid unhandled exceptions that don't terminate the process (e.g. Flutter)
no longer end the session as crashed. The session is kept alive and marked
pending-unhandled, finalized as unhandled on session end or escalated to
crashed on a native crash. Existing captureEnvelope(byte[], boolean) behavior
(React Native) is unchanged.

Co-authored-by: Cursor <cursoragent@cursor.com>
Keep the existing hybrid capture path isolated from non-terminating Flutter
session handling while retaining pending-unhandled session persistence.

Co-authored-by: Cursor <cursoragent@cursor.com>
Serialize pending session mutations with scope and cache lifecycle operations
so delayed session updates cannot overwrite or delete newer session state.

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

Added unreleased section with recent fixes for ANR and crash events.
@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor
Messages
📖 Do not forget to update Sentry-docs with your feature once the pull request gets approved.

Generated by 🚫 dangerJS against c38c9f9

@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

Copy link
Copy Markdown
Contributor Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit c6f705c. Configure here.

@sentry

sentry Bot commented Jul 21, 2026

Copy link
Copy Markdown

📲 Install Builds

Android

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

⚙️ sentry-android Build Distribution Settings

@buenaflor

Copy link
Copy Markdown
Contributor Author

@sentry review

Comment on lines +118 to +126
try (final @NotNull ISentryLifecycleToken ignored = sessionLock.acquire()) {
final @Nullable Session endingSession = readSessionFromEnvelope(envelope);
final @Nullable Session currentSession = readSessionFromDisk(currentSessionFile);
if (endingSession != null
&& currentSession != null
&& hasSameSessionId(endingSession, currentSession)
&& !currentSessionFile.delete()) {
options.getLogger().log(WARNING, "Current envelope doesn't exist.");
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bug: The new logic for SessionEnd no longer unconditionally deletes the current session file. If reading the session from the envelope fails, a stale session file will be left on disk.
Severity: MEDIUM

Suggested Fix

Revert to the previous behavior of unconditionally deleting the currentSessionFile when handling a SessionEnd event. Alternatively, ensure the file is deleted even if parsing the ending session from the envelope fails, for instance, by moving the deletion logic outside the conditional check.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: sentry/src/main/java/io/sentry/cache/EnvelopeCache.java#L118-L126

Potential issue: On `SessionEnd`, the current session file is only deleted if the ending
session can be successfully read from the envelope. Previously, this deletion was
unconditional. If `readSessionFromEnvelope()` returns `null` due to a parsing error, an
empty envelope, or an incorrect item type, the new logic will fail to delete the session
file. This leaves a stale session file on disk, which can be picked up by
`PreviousSessionFinalizer` on the next app start, leading to incorrect session state
recovery and potentially skewing crash-free session rate metrics.

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

Check for an unhandled exception directly instead of using the crash-named
predicate in a path where the process intentionally continues.

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

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