Skip to content

fix(android): Prevent fatal 'JavaCallback was already settled' crash from getNewScreenTimeToDisplay - #6722

Open
antonis wants to merge 4 commits into
mainfrom
fix/ttid-javacallback-already-settled
Open

fix(android): Prevent fatal 'JavaCallback was already settled' crash from getNewScreenTimeToDisplay#6722
antonis wants to merge 4 commits into
mainfrom
fix/ttid-javacallback-already-settled

Conversation

@antonis

@antonis antonis commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

📢 Type of change

  • Bugfix
  • New feature
  • Enhancement
  • Refactoring

📜 Description

On Android, RNSentryTimeToDisplay.getTimeToDisplay (reached from the getNewScreenTimeToDisplay TurboModule method) settles the JS promise from a deferred Choreographer frame callback. Under rapid navigation the bridge callback backing that promise can already have been settled/invalidated by the runtime by the time the frame callback runs — on Expo bridgeless (new architecture) this surfaces as JavaCallback was already settled. Cannot invoke it again. Because the settle happens inside the frame callback, the throw was uncaught and reached the host app as a fatal crash.

This routes every settle through guarded helpers (resolveSafely / rejectSafely) that:

  • settle the promise at most once (AtomicBoolean guard), and
  • swallow any settle failure, logging at WARNING instead of propagating.

A late/duplicate settle now drops the time-to-display measurement (the expected degradation) instead of crashing the app. The implementation is shared between the old and new architecture, so the fix covers both.

💡 Motivation and Context

Fixes #6709

💚 How did you test it?

New Android unit tests in RNSentryTimeToDisplayTest:

  • Manula testing
  • Added tests

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

🔮 Next steps

antonis and others added 2 commits September 14, 2026 12:50
…from getNewScreenTimeToDisplay

`RNSentryTimeToDisplay.getTimeToDisplay` settles the promise from a deferred
Choreographer frame callback. Under rapid navigation the bridge callback backing
the promise can already have been settled/invalidated by the runtime by the time
the frame callback runs (on Expo bridgeless this surfaces as "JavaCallback was
already settled"). Because the settle happened inside the frame callback, the
throw was uncaught and reached the host app as a fatal crash.

Route every settle through guarded helpers that settle at most once and swallow
any settle failure (logging at WARNING), so a late/duplicate settle drops the
time-to-display measurement instead of crashing the app. Shared impl, so this
covers both the old and new architecture.

Fixes #6709

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 14, 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): Prevent fatal 'JavaCallback was already settled' crash from getNewScreenTimeToDisplay by antonis in #6722
  • fix(tracing): background root spans overwrite native propagation context by alwx in #6720
  • ci(e2e): Fix broken idb-companion install on iOS e2e by antonis in #6719
  • chore(deps): Bump smol-toml to ^1.7.1 by antonis in #6713
  • chore(deps): Bump js-yaml to patched versions to resolve security alerts by antonis in #6710
  • chore(deps): Bump morgan to ^1.12.0 to resolve security alert by antonis in #6712
  • chore(deps): Bump joi to ^17.13.6 by antonis in #6714
  • chore(deps): Bump sharp to ^0.35.4 to resolve security alert by antonis in #6711
  • chore(deps): bump actions/setup-java from 6.0.0 to 6.0.1 by dependabot in #6716
  • chore(deps): bump getsentry/craft/.github/workflows/changelog-preview.yml from 2.30.1 to 2.31.0 by dependabot in #6718
  • chore(deps): bump getsentry/craft from 2.30.1 to 2.31.0 by dependabot in #6717
  • chore(deps): bump the codeql-action group with 3 updates by dependabot in #6715
  • fix(core): Fix process.exit typo in debugid script by friederbluemle in #6707
  • chore(ci): Only build production builds for visionOS and tvOS by antonis in #6700
  • chore(deps): update Sentry Android Gradle Plugin to v6.22.0 by github-actions in #6699

🤖 This preview updates automatically when you update the PR.

@antonis antonis added the ready-to-merge Triggers the full CI test suite label Sep 14, 2026
Comment thread packages/core/android/src/main/java/io/sentry/react/RNSentryTimeToDisplay.java Outdated

@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 d16b9da. Configure here.

@sentry

sentry Bot commented Sep 14, 2026

Copy link
Copy Markdown

📲 Install Builds

Android

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

⚙️ sentry-react-native Build Distribution Settings

@github-actions

github-actions Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Android (new) Performance metrics 🚀

  Plain With Sentry Diff
Startup time 469.82 ms 527.88 ms 58.06 ms
Size 50.56 MiB 56.49 MiB 5.93 MiB

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
f3215d3+dirty 396.53 ms 436.66 ms 40.13 ms
d0e3b3e+dirty 443.19 ms 480.00 ms 36.81 ms
a5d243c+dirty 499.20 ms 525.62 ms 26.42 ms
7fd0012+dirty 422.41 ms 445.57 ms 23.17 ms
0a147b2+dirty 442.80 ms 522.24 ms 79.44 ms
d7d54c6+dirty 423.54 ms 498.56 ms 75.02 ms
c823bb5+dirty 468.26 ms 516.16 ms 47.90 ms
5789645+dirty 502.66 ms 594.19 ms 91.53 ms
40c9884+dirty 453.76 ms 478.98 ms 25.22 ms
d771cd5+dirty 432.29 ms 464.38 ms 32.09 ms

App size

Revision Plain With Sentry Diff
f3215d3+dirty 48.30 MiB 53.49 MiB 5.19 MiB
d0e3b3e+dirty 49.74 MiB 55.09 MiB 5.34 MiB
a5d243c+dirty 48.30 MiB 53.54 MiB 5.23 MiB
7fd0012+dirty 50.56 MiB 56.46 MiB 5.90 MiB
0a147b2+dirty 49.74 MiB 55.08 MiB 5.34 MiB
d7d54c6+dirty 49.74 MiB 55.09 MiB 5.34 MiB
c823bb5+dirty 48.30 MiB 53.58 MiB 5.28 MiB
5789645+dirty 49.74 MiB 54.85 MiB 5.11 MiB
40c9884+dirty 49.74 MiB 55.09 MiB 5.34 MiB
d771cd5+dirty 49.74 MiB 55.36 MiB 5.62 MiB

…lures

Switch the null-cause branch of rejectSafely from the deprecated
reject(String) overload to reject(code, message) so JS receives a
specific error code ("SentryTimeToDisplay") instead of the generic
"EUNSPECIFIED". Update the affected tests to match the two-arg overload.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Android (legacy) Performance metrics 🚀

  Plain With Sentry Diff
Startup time 417.50 ms 438.94 ms 21.44 ms
Size 50.56 MiB 56.49 MiB 5.93 MiB

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
37a2091+dirty 407.82 ms 441.22 ms 33.40 ms
f3215d3+dirty 411.11 ms 454.38 ms 43.27 ms
bc8f61e+dirty 442.98 ms 506.56 ms 63.58 ms
f9c1ed4+dirty 431.00 ms 466.22 ms 35.22 ms
7887847+dirty 416.61 ms 462.04 ms 45.43 ms
57e0069+dirty 413.82 ms 453.47 ms 39.65 ms
a0a3177+dirty 441.27 ms 499.86 ms 58.59 ms
09a902f+dirty 427.38 ms 473.80 ms 46.42 ms
a5d243c+dirty 424.52 ms 485.18 ms 60.66 ms
5a23c47+dirty 423.85 ms 466.47 ms 42.62 ms

App size

Revision Plain With Sentry Diff
37a2091+dirty 48.30 MiB 53.58 MiB 5.28 MiB
f3215d3+dirty 48.30 MiB 53.49 MiB 5.19 MiB
bc8f61e+dirty 49.74 MiB 55.09 MiB 5.35 MiB
f9c1ed4+dirty 49.74 MiB 54.86 MiB 5.12 MiB
7887847+dirty 49.74 MiB 54.81 MiB 5.07 MiB
57e0069+dirty 49.74 MiB 54.85 MiB 5.11 MiB
a0a3177+dirty 49.74 MiB 55.37 MiB 5.63 MiB
09a902f+dirty 49.74 MiB 54.81 MiB 5.07 MiB
a5d243c+dirty 48.30 MiB 53.54 MiB 5.23 MiB
5a23c47+dirty 49.74 MiB 54.82 MiB 5.07 MiB

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.

Fatal 'JavaCallback was already settled' from getNewScreenTimeToDisplay under rapid navigation (Android, Expo, new arch)

1 participant