Filed by Sentry Support on behalf of a customer. Internal reference: Intercom conversation.
What React Native libraries do you use?
React Navigation, Hermes, RN New Architecture, Expo (mobile only), Expo Application Services (EAS)
Are you using sentry.io or on-premise?
sentry.io (SaaS)
Are you using any other error monitoring solution alongside Sentry?
No
@sentry/react-native SDK Version
8.2.0. Also reproduces against the current develop branch.
How does your development environment look like?
React Native: 0.83.6
Hermes: 0.14.1 (enabled)
New Architecture: Fabric + Turbo Modules (enabled)
Expo: yes
Target: Android (observed in production)
Sentry.init()
Sentry.init({
// ...
integrations: [
Sentry.reactNativeTracingIntegration(),
Sentry.reactNavigationIntegration({
enableTimeToInitialDisplay: true,
useDispatchedActionData: true,
}),
],
});
Steps to Reproduce
Navigation hierarchy:
NavigationContainer
└── Stack
└── Tab
└── Stack
└── ScreenA (initial route)
- Cold start the app on Android with the navigation hierarchy above and
enableTimeToInitialDisplay: true on reactNavigationIntegration.
- Navigate into a sibling screen inside the inner stack so
ScreenA's ScreenStackFragment view is destroyed or freezeOnBlur'd but the fragment remains cached behind the tab.
- Background the app. With Hermes on Android the JS thread is suspended.
- Foreground the app some time later (minutes to days).
- POP back to
ScreenA. The fragment view is recreated, RNSentryReactFragmentLifecycleTracer.onFragmentViewCreated re-attaches its ScreenAppearEvent listener, the event fires, and FirstDrawDoneListener.registerForNextDraw captures the current draw timestamp into RNSentryTimeToDisplay against the currently-active span ID.
- The JS-side idle navigation span finishes with
sentry.idle_span_finish_reason: idleTimeout.
timeToDisplayIntegration.processEvent reads the stored TTID via popTimeToDisplayFor and unconditionally extends event.timestamp = max(ttid.timestamp, ttfd.timestamp, event.timestamp).
Expected Result
- The
ui.load.initial_display end timestamp is bounded. If the captured TTID is past a sensible deadline relative to start_timestamp, the SDK either drops the TTID, marks it deadline_exceeded, or refuses to extend event.timestamp with it.
- The root
navigation transaction duration reflects the actual screen render time, not the wall-clock elapsed since navigation started.
Actual Result
The root navigation transaction is sent with a duration of hours or days. The captured TTID has the same start/end as the root, and event.timestamp is extended to match the TTID end. Trace excerpt:
{
"op": "navigation",
"start_timestamp": 1778936730.088075,
"timestamp": 1779346256.41,
"data": {
"sentry.idle_span_finish_reason": "idleTimeout",
"navigation.action_type": "POP",
"route.name": "ScreenA"
}
}
{
"op": "ui.load.initial_display",
"description": "com.swmansion.rnscreens.ScreenStackFragment initial display",
"origin": "auto.ui.time_to_display",
"start_timestamp": 1778936730.088075,
"timestamp": 1779346256.41
}
The navigation.processing child closed at start + 29 ms. All http.client children closed within roughly 1 s. Only the TTID is pathological. Observed at production scale on Sentry SaaS as a small but non-trivial fraction (well under 1%) of navigation transactions exceeding 1 hour duration, 100% on Android, concentrated on screens behind a Stack > Tab > Stack shape.
Workaround
Sentry.init({
// ...
beforeSendTransaction(event) {
const start = event.start_timestamp ?? 0;
const end = event.timestamp ?? 0;
if (event.contexts?.trace?.op === 'navigation' && end - start > 60) {
return null;
}
return event;
},
});
Safe on 8.2.0 and on develop, no effect on healthy traces, immediately stops inflated transactions from polluting Sentry aggregates and alerts.
Filed by Sentry Support on behalf of a customer. Internal reference: Intercom conversation.
What React Native libraries do you use?
React Navigation, Hermes, RN New Architecture, Expo (mobile only), Expo Application Services (EAS)
Are you using sentry.io or on-premise?
sentry.io (SaaS)
Are you using any other error monitoring solution alongside Sentry?
No
@sentry/react-native SDK Version
8.2.0. Also reproduces against the current
developbranch.How does your development environment look like?
Sentry.init()
Steps to Reproduce
Navigation hierarchy:
enableTimeToInitialDisplay: trueonreactNavigationIntegration.ScreenA'sScreenStackFragmentview is destroyed orfreezeOnBlur'd but the fragment remains cached behind the tab.ScreenA. The fragment view is recreated,RNSentryReactFragmentLifecycleTracer.onFragmentViewCreatedre-attaches itsScreenAppearEventlistener, the event fires, andFirstDrawDoneListener.registerForNextDrawcaptures the current draw timestamp intoRNSentryTimeToDisplayagainst the currently-active span ID.sentry.idle_span_finish_reason: idleTimeout.timeToDisplayIntegration.processEventreads the stored TTID viapopTimeToDisplayForand unconditionally extendsevent.timestamp = max(ttid.timestamp, ttfd.timestamp, event.timestamp).Expected Result
ui.load.initial_displayend timestamp is bounded. If the captured TTID is past a sensible deadline relative tostart_timestamp, the SDK either drops the TTID, marks itdeadline_exceeded, or refuses to extendevent.timestampwith it.navigationtransaction duration reflects the actual screen render time, not the wall-clock elapsed since navigation started.Actual Result
The root
navigationtransaction is sent with a duration of hours or days. The captured TTID has the same start/end as the root, andevent.timestampis extended to match the TTID end. Trace excerpt:{ "op": "navigation", "start_timestamp": 1778936730.088075, "timestamp": 1779346256.41, "data": { "sentry.idle_span_finish_reason": "idleTimeout", "navigation.action_type": "POP", "route.name": "ScreenA" } }{ "op": "ui.load.initial_display", "description": "com.swmansion.rnscreens.ScreenStackFragment initial display", "origin": "auto.ui.time_to_display", "start_timestamp": 1778936730.088075, "timestamp": 1779346256.41 }The
navigation.processingchild closed atstart + 29 ms. Allhttp.clientchildren closed within roughly 1 s. Only the TTID is pathological. Observed at production scale on Sentry SaaS as a small but non-trivial fraction (well under 1%) of navigation transactions exceeding 1 hour duration, 100% on Android, concentrated on screens behind aStack > Tab > Stackshape.Workaround
Safe on 8.2.0 and on
develop, no effect on healthy traces, immediately stops inflated transactions from polluting Sentry aggregates and alerts.