getsentry / getsentry/sentry-react-native

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

Cerrado
#6,709 2 comentarios 0 reacciones 1 asignado Reclamado por @antonis Ver en GitHub
Bug React-Native
Lenguaje dominante
TypeScript
Estrellas
1.8k
Forks
366
Merge medio
1 d 6 h
PR fusionados (30 d)
94

Descripción

### OS

Android

### Platform

Hermes, New Architecture

### SDK Version

8.19.0

### React Native Version

0.85.3 (Expo SDK 56, expo-modules-core 56.0.19)

### Steps to Reproduce

Not deterministic — it is a race, but the shape that reproduces it in our production fleet:

1. `Sentry.init` with `reactNavigationIntegration({enableTimeToInitialDisplay: true})`, container registered via `registerNavigationContainer` (expo-router).
2. App with 4 native bottom tabs (`react-native-bottom-tabs`) sharing a header.
3. Switch tabs rapidly and/or push a modal right after a tab switch (our events consistently show a paywall modal being pushed immediately after navigation — breadcrumbs end with the new screen's mount logs).

### Expected Result

TTID measurement completes or is dropped silently when navigations race.

### Actual Result

Fatal crash reported through `onerror`:

```
Error: Non-js exception: JavaCallback was already settled. Cannot invoke it again
```

Event tags point at the SDK's own TurboModule call:

- `turbo_module.name: RNSentry`
- `turbo_module.method: getNewScreenTimeToDisplay`
- `mechanism: onerror`, `handled: no`, `level: fatal`, Hermes, Android 12–16

Volume: 23 users in 4 days in production (it likely existed longer — our DSN was broken before that, so the SDK could not report its own crash).

### Analysis

`reactnavigation.ts` (`updateLatestNavigationSpanWithCurrentRoute`) calls `NATIVE.getNewScreenTimeToDisplay()` on **every** navigation state change while `enableTimeToInitialDisplay` is enabled. The Android implementation appears to keep a single stored callback to be settled on the next frame; when two navigation state changes race, the same callback gets settled twice. On Expo (new architecture) the promise guard in `expo-modules-core` (`PromiseImpl` — "was already settled") turns the second settle into the fatal above.

No matching fix found in the changelog up to 8.26.0, and no existing issue mentions `getNewScreenTimeToDisplay`, so filing this one.

### Workaround

`enableTimeToInitialDisplay: false` removes the crashing path entirely (confirmed: the call is gated only by that option).

Guía de contribución

Abrir la guía de contribución

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.