Skip to content

ui.load.initial_display spans reach transaction serialization without Relay-required fields #6597

Description

@0x80

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.

Metadata

Metadata

Assignees

Projects

Status
No status

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions