Skip to content

fix(android): Persist hard-crash envelopes to disk before the process exits - #6937

Merged
alwx merged 4 commits into
mainfrom
alwx/bugfix/6918
Oct 27, 2026
Merged

alwx merged 4 commits into
mainfrom
alwx/bugfix/6918

Conversation

@alwx

@alwx alwx commented Oct 23, 2026

Copy link
Copy Markdown
Contributor

📢 Type of change

  • Bugfix
  • New feature
  • Enhancement
  • Refactoring

📜 Description

When hardCrashed is true, RNSentryModuleImpl.captureEnvelope now writes the envelope to options.getEnvelopeDiskCache() on the calling thread instead of queueing it on the transport executor. It applies the same session handling as InternalSentrySdk.captureEnvelope (end the session as crashed, attach it, delete the current-session file). The next launch sends the cached file, which matches the iOS store path. Without a disk cache the old queued path runs, and if the store fails the envelope is queued instead.

💡 Motivation and Context

InternalSentrySdk.captureEnvelope only submits the envelope to the single-threaded transport executor. React Native then rethrows the fatal as JavascriptException, which is an ignored exception type, so the native SDK skips the disk flush and the process dies with the envelope still in memory. In release builds most fatal JS errors never reached Sentry.

Fixes #6918

💚 How did you test it?

Added RNSentryCaptureEnvelopeTest with five cases (sync store, crashed session attached, fallback when the store fails, no disk cache, non-fatal path). Three of them fail on main and all pass with this change. All Android unit tests and yarn lint:android pass.

📝 Checklist

  • I added tests to verify 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.
  • All tests passing.
  • Public API changes reviewed by another Mobile SDK team member or implemented according to the develop docs spec.
  • No breaking changes.

@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Semver Impact of This PR

⚪ None (no version bump detected)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


  • fix(android): Persist hard-crash envelopes to disk before the process exits by alwx in #6937
  • feat(tracing): Track WebView trace-propagation feature adoption by antonis in #6938
  • build(deps): Bump body-parser to 2.4.0 to resolve DoS advisory by antonis in #6948
  • fix(android): Apply screenshot masking options to error and feedback screenshots by antonis in #6930
  • chore(deps): update Android SDK to v8.62.0 by github-actions in #6932
  • chore(deps): update Maestro to v2.12.0 by github-actions in #6945
  • chore(deps): bump getsentry/craft/.github/workflows/changelog-preview.yml from 2.35.0 to 3.0.0 by dependabot in #6944
  • chore(deps): bump the codeql-action group with 3 updates by dependabot in #6940
  • chore(deps): bump actions/upload-artifact from 7.0.1 to 7.0.2 by dependabot in #6941
  • chore(deps): bump fast-uri from 3.1.8 to 3.1.9 by dependabot in #6946
  • chore(deps): bump undici from 6.29.0 to 6.29.1 by dependabot in #6947
  • chore(deps): bump actions/setup-node from 7.0.0 to 7.1.0 by dependabot in #6943
  • chore(deps): bump ruby/setup-ruby from 1.327.0 to 1.334.0 by dependabot in #6942
  • chore(deps): bump ip-address from 10.7.2 to 10.7.4 by dependabot in #6939
  • chore(deps): update Wizard to v9.0.0 by github-actions in #6936
  • chore(deps): update CLI to v3.8.2 by github-actions in #6933
  • ci: Move CI off end-of-life Node versions to Node 22 by antonis in #6927

🤖 This preview updates automatically when you update the PR.

Comment thread packages/core/android/src/main/java/io/sentry/react/RNSentryModuleImpl.java Outdated
@GrahamCampbell

Copy link
Copy Markdown

Small thing on the "matching iOS" part: iOS only takes the store path in release builds. Under #if DEBUG, captureEnvelope still calls capture, so a fatal JS error in a debug build is sent right away while LogBox keeps the app running. There's no such gate here, so in an Android debug build that event now sits in the cache until the SDK is initialized again or connectivity changes. Should the Android store be limited to release builds too?

alwx added 3 commits October 27, 2026 11:31
… exits

A fatal JS error was captured through InternalSentrySdk.captureEnvelope,
which only queues the envelope on the single-threaded transport executor.
React Native then rethrows the error as JavascriptException, an ignored
exception type, so UncaughtExceptionHandlerIntegration skips the disk
flush and the process dies with the envelope still in memory. In release
builds most fatal JS errors never reached Sentry.

When hardCrashed is set, read the envelope, end the current session as
crashed and attach it (the same session handling as
InternalSentrySdk.captureEnvelope), delete the current-session file, and
write the envelope to options.getEnvelopeDiskCache() on the calling
thread. The next launch sends the cached file, which matches the iOS
store path. Without a disk cache, or when the envelope cannot be read,
the previous queued path runs. When the store itself fails, the
repackaged envelope is queued instead so only one copy exists.

Fixes #6918
Address review feedback on the hard-crash store path.

The current-session file was deleted before the envelope was written to
the disk cache. If the store failed, the envelope was only queued while
the file was already gone, so the next launch could not report the
session as abnormal. The file is now deleted only after storeEnvelope
succeeds.

Debuggable builds now skip the store path and capture directly, like the
iOS #if DEBUG gate. In debug builds LogBox keeps the app alive, so the
event would otherwise sit in the cache until the next launch.

Two new tests cover both cases; both fail on the previous code.
@alwx
alwx force-pushed the alwx/bugfix/6918 branch from dc4fb97 to dd2cc62 Compare October 27, 2026 10:40
@alwx

alwx commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor Author

Good point, thanks. Added the gate so now when the host app has ApplicationInfo.FLAG_DEBUGGABLE, captureEnvelope keeps the direct capture path and a debug build sends the fatal right away while LogBox keeps the app alive.

@alwx
alwx marked this pull request as ready for review October 27, 2026 10:46
@antonis antonis added the ready-to-merge Triggers the full CI test suite label Oct 27, 2026

@antonis antonis left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM once the PR is 🟢

@sentry

sentry Bot commented Oct 27, 2026

Copy link
Copy Markdown

📲 Install Builds

Android

🔗 App Name App ID Version Configuration
Sentry RN io.sentry.reactnative.sample 8.32.0 (112) Release

⚙️ sentry-react-native Build Distribution Settings

@alwx
alwx merged commit 18da629 into main Oct 27, 2026
117 of 123 checks passed
@alwx
alwx deleted the alwx/bugfix/6918 branch October 27, 2026 13:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-to-merge Triggers the full CI test suite

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Android] Fatal JS errors lost in release builds: hard-crash envelope is only queued, then JavascriptException is dropped without a flush

3 participants