Skip to content

[RUM] Core Web Vitals collection is dead — LCP/FCP dropped 55% → 0% of initial loads around 2026-07-04, blinding #1405 #1570

Description

@dawsontoth

Summary

Studio has not reported a single LCP or FCP measurement in over three weeks. Coverage of initial_load views collapsed from ~55% to exactly 0% around 2026-07-04 and has stayed there.

This means #1405 (open — "LCP performance regression: '/' view degraded +61.7%, 4320ms p75") can no longer be measured, confirmed, or closed, and no future LCP/FCP regression can be detected at all.

Surfaced by the automated daily RUM review (app f590deee-…).

Datadog findings

Coverage = share of @type:view @view.loading_type:initial_load events carrying the metric:

Metric ~2026-06-26 → 06-30 ~2026-07-04 → 07-06 last 24h
@view.largest_contentful_paint 85/156 = 54.5% 0/61 = 0.0% 0/160 = 0.0%
@view.first_contentful_paint 89/156 = 57.1% 0/61 = 0.0% 0/160 = 0.0%
@view.dom_complete 98/156 = 62.8% 5/61 = 8.2% 39/160 = 24.4%
@view.loading_time 0/156 = 0.0% 0/61 = 0.0% 0/160 = 0.0%

@view.first_input_delay and @view.interaction_to_next_paint also return no data.

The signature is precise

dom_complete still works. LCP and FCP are exactly zero.

dom_complete comes from PerformanceNavigationTiming; LCP and FCP come from PerformanceObserver entries. Views are still being created and still carry navigation timings — so this is not a sampling, ingestion, or view-tracking outage. Specifically the paint-observer metrics stopped reaching the view.

A "0 out of 160" is not sparsity: at the old ~55% rate we'd expect ~88.

Narrowing the break

Two-day buckets put the transition between ~2026-07-02→07-04 (39.8% coverage) and ~2026-07-04→07-06 (0.0%):

window (rel. to 2026-07-28) initial loads with LCP pct
32d → 28d 156 85 54.5%
28d → 26d 287 152 53.0%
26d → 24d 98 39 39.8%
24d → 22d 60 0 0.0%
22d → 20d 298 6 2.0%
18d → 16d 112 1 0.9%
14d → 12d 263 6 2.3%
8d → 6d 252 3 1.2%

(The residual 1–2% after the break is a handful of stragglers, presumably long-lived tabs on older bundles.)

Not the SDK. @datadog/browser-rum resolved to 7.4.0 on both sides of that boundary (7.2.0 Jun 19 → 7.3.0 Jun 22 → 7.4.0 Jun 29 → 7.5.0 Jul 13 → 7.6.1 Jul 27). Prod is currently on sdk_version:7.5.0. The break predates the 7.5.0 bump by ~10 days.

Likely mechanism

src/integrations/datadog/datadog.ts initializes with trackViewsManually: true, then starts a view twice during startup:

  1. useDatadog (called from src/App.tsx) inits RUM inside a useEffect — i.e. after React mounts — and on onReady calls startView({ name: window.location.pathname || 'initial' }).
  2. useOnRouteLoadTracker (called from src/components/StudioCloud.tsx) then calls startView again with the translated route name, on an effect keyed [location.href, router].

With trackViewsManually, Datadog attributes LCP/FCP to the initial view, and stops collecting once that view ends. If the second startView replaces the initial view before the browser settles the largest paint, both metrics are dropped — which matches the data exactly (paint observers zeroed, navigation timings unaffected).

Two commits in the bisect window plausibly changed that startup timing:

  • 4f660718 (2026-07-03) fix(router): create the TanStack router once, not on every AppRouted render — the closest date match, and it directly changes the router identity that useOnRouteLoadTracker's effect is keyed on, hence when the second startView fires. (Context in CLAUDE.md under "the router must be created exactly once".)
  • 7c5e9141 (2026-07-06) fix(auth): subscribe useRootAuthenticationContext once, not per change — also changes render/effect cadence around startup.

I want to be clear this is a hypothesis, not a proven cause — the correlation is by date and mechanism, and I could not reproduce paint-metric collection locally (it needs a production build and real page loads).

Suggested next steps

  1. Confirm the mechanism with a production-mode build: init RUM, log datadogRum.getInternalContext() view ids across the two startView calls, and check whether the initial view ends before LCP settles.
  2. Don't replace the initial view. Either drop the startView in useDatadog and let useOnRouteLoadTracker name the first view, or keep the initial view and only startView on subsequent route changes. One view per page load at startup, not two.
  3. Consider initializing RUM outside a useEffect — at module scope in the entry, before React mounts — so the SDK's observers attach as early as possible.
  4. Re-evaluate trackViewsManually: true. If it's only there for the friendly route names, @datadog/browser-rum-react's router instrumentation may give both naming and correct vitals.
  5. Add a guard against silent loss: a Datadog monitor on LCP sample count per day would have caught this three weeks ago. Right now nothing alerts when a metric simply stops arriving.
  6. Once fixed, revisit [RUM] LCP performance regression: '/' view degraded +61.7% in last 24h (4320ms p75) #1405 — its p75 baseline predates the break, so it needs fresh data before it can be judged.

Related

Filed by the automated daily Datadog RUM review. No PII or customer identifiers included.

Metadata

Metadata

Assignees

Labels

rumFrom real user monitoring where we aim to keep users happy

Type

No type

Fields

Priority

None yet

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions