Summary
On iOS with @sentry/react-native 8.19.0, ui.load.initial_display spans frequently reach beforeSendTransaction without every field Relay requires. Sending one of these children causes Relay to reject the entire transaction as invalid_transaction, including its attached Hermes profile.
Our application-side transaction-integrity guard drops structurally incomplete children before transport and records the affected operation in a tx_integrity_ops tag. Dashboard data over 30 days shows:
dropped:ui.load.initial_display: 689 transactions
clamped:http.client: 1 transaction
The ui.load.initial_display result is from the dashboard facet, not inferred from SDK source. It is the dominant repair by a wide margin. Of those 689 transactions, 658 were still named Route Change; the rest covered ordinary named routes, including /events/[eventId].
Without the guard, Relay rejects the complete transaction. With the guard, Sentry ingested 3,439 transactions in the same window. A fresh physical-device session after enabling UI Profiling produced 96 accepted profiles, including multiple /events/[eventId] profiles, which confirms that removing the malformed child preserves both the transaction and its profile.
Environment
@sentry/react-native: 8.19.0
- React Native: 0.86.0
- Hermes: enabled
- New Architecture / Fabric / TurboModules: enabled
- Device: physical iPhone17,1
- iOS: observed on 26.5.2 and 26.6
- Navigation: Expo Router / React Navigation integration
enableTimeToInitialDisplay: enabled
Relevant SDK path
startTimeToInitialDisplaySpan starts an inactive ui.load.initial_display child at the root span's start time. updateInitialDisplaySpan ends it only after the native display timestamp arrives. There is therefore a window where the navigation root can serialize while the TTID child remains incomplete. The background-cancel path also ends only recording http.client children before ending the root.
Our dashboard tag proves that ui.load.initial_display is incomplete at serialization. It does not distinguish which required field is absent, so I do not want to overstate that part without an SDK-side reproduction.
Expected behavior
Before a navigation transaction is serialized, the SDK should either finish an outstanding auto-instrumented ui.load.initial_display child with a valid end timestamp or omit that child. An unfinished TTID child must not invalidate the root transaction and its profile.
Workaround
In beforeSendTransaction, discard any ui.load.initial_display child missing span_id, trace_id, a finite start_timestamp, or a finite timestamp.
Summary
On iOS with
@sentry/react-native8.19.0,ui.load.initial_displayspans frequently reachbeforeSendTransactionwithout every field Relay requires. Sending one of these children causes Relay to reject the entire transaction asinvalid_transaction, including its attached Hermes profile.Our application-side transaction-integrity guard drops structurally incomplete children before transport and records the affected operation in a
tx_integrity_opstag. Dashboard data over 30 days shows:dropped:ui.load.initial_display: 689 transactionsclamped:http.client: 1 transactionThe
ui.load.initial_displayresult is from the dashboard facet, not inferred from SDK source. It is the dominant repair by a wide margin. Of those 689 transactions, 658 were still namedRoute Change; the rest covered ordinary named routes, including/events/[eventId].Without the guard, Relay rejects the complete transaction. With the guard, Sentry ingested 3,439 transactions in the same window. A fresh physical-device session after enabling UI Profiling produced 96 accepted profiles, including multiple
/events/[eventId]profiles, which confirms that removing the malformed child preserves both the transaction and its profile.Environment
@sentry/react-native: 8.19.0enableTimeToInitialDisplay: enabledRelevant SDK path
startTimeToInitialDisplaySpanstarts an inactiveui.load.initial_displaychild at the root span's start time.updateInitialDisplaySpanends it only after the native display timestamp arrives. There is therefore a window where the navigation root can serialize while the TTID child remains incomplete. The background-cancel path also ends only recordinghttp.clientchildren before ending the root.Our dashboard tag proves that
ui.load.initial_displayis incomplete at serialization. It does not distinguish which required field is absent, so I do not want to overstate that part without an SDK-side reproduction.Expected behavior
Before a navigation transaction is serialized, the SDK should either finish an outstanding auto-instrumented
ui.load.initial_displaychild with a valid end timestamp or omit that child. An unfinished TTID child must not invalidate the root transaction and its profile.Workaround
In
beforeSendTransaction, discard anyui.load.initial_displaychild missingspan_id,trace_id, a finitestart_timestamp, or a finitetimestamp.