Skip to content

Add API to extend app start (React Native) #6303

Closed

Description

@buenaflor

Rewrite RN's app start end customization onto a span-returning API consistent with Flutter, Android and Cocoa. Today appLoaded() only moves the end timestamp and returns nothing, so users can't break down extended launch work.

// Simple: extend the app start window, then finish
Sentry.extendAppStart();
await initializeRemoteConfig();
Sentry.finishExtendedAppStart();
// With child spans: instrument the extended region
Sentry.extendAppStart();
const span = Sentry.getExtendedAppStartSpan();

const configSpan = span.startChild({ op: 'app.init', description: 'fetch remote config' });
await loadRemoteConfig();
configSpan.finish();

Sentry.finishExtendedAppStart();

Reference: getsentry/sentry-cocoa#6886 (Done, SentryExtendedAppLaunchManager). Siblings: getsentry/sentry-dart#3767, getsentry/sentry-java#5553 — shared contract, RN-specific sections below.

API

  • extendAppStart(): void — creates the extended app start span (op app.start.extended, description "Extended App Start").
  • finishExtendedAppStart — finishes it (returns void); triggers finalization.
  • getExtendedAppStartSpan(): Span — returns the span for attaching children; a no-op span when there's no active extension.

finishExtendedAppStart is equivalent to calling .finish() on the returned span.

Lifecycle

  • extendAppStart: creates the span (start = call time). No-ops if app start already finished, if none is in progress, or on repeat calls (first wins).
  • getExtendedAppStartSpan: returns the extended span, else a no-op span.
  • finishExtendedAppStart: no-ops if not extended or called twice. Doesn't finalize directly — the parent (auto-generated, waitForChildren) finalizes when its last child finishes and trims its end to that child. Open children of the extended span are finished cancelled at the finish-call time.

Duration & measurement

  • Parent duration = end of the last child to finish (trim-to-last-child), floored at the default app start end — i.e. end = max(last child end, default app start end). Extending can only push the end later; finishing before the default end keeps the default (never shorter than a non-extended app start). The extended span is just one participant.
  • App start measurement = that final duration (process start → last child), set at finalization. The normal completion-time measurement must be suppressed in extended mode so the early (non-extended) value doesn't win.

Timeout

  • Parent's auto-finish deadline is 30s. If finishExtendedAppStart is never called, the transaction auto-finishes on the deadline (unfinished children → deadlineExceeded, snapped to transaction end), is still captured, but the app.vitals.start measurement is suppressed — never emit a ~30s app start. Same for standalone and ui.load.

Standalone & non-standalone

  • Both supported, same mechanism: standalone uses the app-start tracer, non-standalone the ui.load transaction (also waitForChildren + deadline). In non-standalone the duration is bounded by anything keeping ui.load open (TTID, other children), so it can include spans unrelated to the extension.

RN specifics

  • Replace appLoaded() directly (experimental — no deprecation cycle) and fold in the already-deprecated captureAppStart(). Document the migration to extendAppStart / children / finishExtendedAppStart.
  • RN's waitForChildren equivalent is the cancel-deferred path: auto-capture schedules captureStandaloneAppStart() via setTimeout(…, 0); extendAppStart() must cancel that deferred send and keep the transaction open until finishExtendedAppStart is called, producing the same trim-to-last-child end. Reuse the cached-response path that bypasses the native has_fetched: true guard.

Acceptance criteria

  • extendAppStart() creates the span before finish; no-ops when too late, not started, or called repeatedly.
  • getExtendedAppStartSpan() returns the span, else a no-op span (including after finish).
  • Custom child spans render under Extended App Start.
  • finishExtendedAppStart no-ops if not extended or called twice; finalizes via the deferred-cancel path with transaction end = last child.
  • Measurement = process start → last child, floored at the default app start end (never shorter than a non-extended app start), set at finalization.
  • Open children at finish → cancelled; on deadline → deadlineExceeded, snapped to end.
  • Deadline path: transaction captured, app.vitals.start suppressed.
  • Extended app starts carry a segmentation attribute.
  • appLoaded() replaced, captureAppStart() folded in, migration documented.
  • Docs updated.

Activity

  1. linear-code commented on Jun 16, 2026

    @linear-code
  2. changed the title [-]Rewrite app start end API to `getExtendedAppStartSpan` (React Native)[/-] [+]Add API to extend app start (React Native)[/+] on Jun 23, 2026
  3. self-assigned this
    on Jun 26, 2026
  4. github-actions commented on Jul 9, 2026

    @github-actions
    Contributor

    A PR closing this issue has just been released 🚀

    This issue was referenced by PR #6392, which was included in the 8.18.0 release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions