Skip to content

feat(tracing): Decouple app start data from navigation transaction lifecycle #5839

@antonis

Description

@antonis

Problem

App start data (cold/warm start measurements) is currently attached to the first sampled navigation transaction as a carrier. This coupling creates several structural problems.

1. Fragile lifecycle dependency

App start data depends on the first navigation transaction surviving its entire lifecycle. That transaction has its own complex lifecycle:

  • Idle timeout / heartbeat expiry
  • Discard logic (ignoreEmptyRouteChangeTransactions, ignoreEmptyBackNavigation)
  • Timing-dependent navigation container registration (registerNavigationContainer)

Any of these can cause the transaction to be dropped, silently discarding app start data with it. This is the root cause of RN-538 (fixed in #5833 as a targeted workaround).

2. Inconsistent sampling behavior

App start data is only sent when a sampled transaction exists to carry it:

  • tracesSampleRate: 1.0 → works reliably
  • tracesSampleRate: 0.1 → app start attaches to the first sampled transaction, which may not be the first navigation event
  • tracesSampleRate: 0 → app start is never sent, even when enableAppStartTracking: true

A user who explicitly enables enableAppStartTracking likely expects to receive this data regardless of general tracing sample rate. The current design silently drops it.

3. Semantic mismatch between carrier and data

App start measurements can end up attached to any root span that happens to be first and sampled — not necessarily a navigation transaction. This makes the data harder to query consistently in Sentry.

Current workaround

#5833 added a lazy-check mechanism: if the locked navigation transaction is later found to be unsampled, the integration falls through to the next transaction. This fixes the specific regression in RN-538 but doesn't address the structural coupling.

Proposed direction

Decouple app start data from the navigation transaction lifecycle. Possible approaches:

  1. Dedicated app start transaction — Send app start as its own transaction (similar to the existing captureStandaloneAppStart path), always when enableAppStartTracking: true, respecting a separate or elevated sample rate.
  2. Guaranteed attachment — Attach app start to the first root span regardless of _sampled status, so sampling decisions don't silently discard it.
  3. Session-level metric — Explore whether app start fits better as a session-attached metric rather than a transaction measurement.

Considerations

  • Any change to sampling behavior for app start may be a breaking change for users who set tracesSampleRate: 0 to opt out of performance data entirely.
  • The captureStandaloneAppStart path already exists and may be a useful foundation.
  • Coordination with other mobile SDKs (Flutter, MAUI, Unity) if the approach is standardized.

Metadata

Metadata

Assignees

No one assigned
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions