Skip to content

Native app start spans missing when TTFD tracking takes longer than 3 seconds #3402

Description

@buenaflor

Note

Will be released in 9.9.1

Description

Native app start spans (Cold Start, Process Initialization, Application.onCreate, etc.) are missing from the "root /" transaction when Time to Full Display (TTFD) tracking takes longer than 3 seconds to complete.

The app_start_cold/app_start_warm measurements are correctly reported, but the detailed span breakdown is missing.

Environment

  • Platform: iOS and Android (both affected)
  • SDK: sentry_flutter
  • Affected versions: At least 9.8.0 (likely earlier versions too)

Root Cause

Race condition in NativeAppStartHandler where _attachAppStartSpans() is called after timeToDisplayTracker.track(), but the transaction can be sent inside the timeToDisplayTracker.track() await chain via _finishedCallback.

Timeline:

  1. Transaction created with autoFinishAfter: 3 seconds
  2. Timer fires at 3s, sets _finishStatus to "finishing"
  3. When TTFD completes (>3s), _finishedCallback triggers finish() and sends the transaction
  4. _attachAppStartSpans() runs after timeToDisplayTracker.track() returns — too late!

What's Present vs Missing

Data Present? Reason
app_start_cold measurement Added before timeToDisplayTracker.track()
app_start_type data Added before timeToDisplayTracker.track()
TTID/TTFD spans Created inside timeToDisplayTracker.track()
Cold Start span Added after (too late)
Native spans (Process Init, etc.) Added after (too late)
First frame render span Added after (too late)

Reproduction

  1. Enable TTFD tracking: options.enableTimeToFullDisplayTracing = true
  2. Call reportFullyDisplayed() after 3+ seconds (or don't call it at all)
  3. Check the "root /" transaction in Sentry
  4. Native app start spans will be missing
// In your app's root widget initState:
Future.delayed(const Duration(seconds: 5), () {
  SentryFlutter.reportFullyDisplayed();
});
// Result: Native app start spans will be MISSING## Suggested Fix

// Move `_attachAppStartSpans()` **before** `timeToDisplayTracker.track()`:

// Current (buggy):
await options.timeToDisplayTracker.track(...);
await _attachAppStartSpans(...);  // Too late!

// Fixed:
await _attachAppStartSpans(...);  // Add spans first
await options.timeToDisplayTracker.track(...);Alternatively, use the `onFinish` callback to ensure spans are attached before the transaction is captured (as mentioned in the existing TODO comment).

Metadata

Metadata

Assignees

Labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions