Skip to content

feat(tracing): Correlate deep links with the navigation they trigger#6264

Merged
alwx merged 5 commits into
mainfrom
alwx/features/deep-linking
Jun 11, 2026
Merged

feat(tracing): Correlate deep links with the navigation they trigger#6264
alwx merged 5 commits into
mainfrom
alwx/features/deep-linking

fix(tracing): Address PR review on deep-link / navigation correlation

a2c3a6a
Select commit
Loading
Failed to load commit list.
@sentry/warden / warden: find-bugs completed Jun 9, 2026 in 24m 38s

2 issues

find-bugs: Found 2 issues (2 medium)

Medium

Warm-open deep link can be consumed by a stale prior navigation span, leaving the triggered navigation span untagged - `packages/core/src/js/integrations/deeplink.ts:111-115`

handleLateDeepLink in reactnavigation.ts is invoked synchronously for every deep link via the registered pendingDeepLink listener, including warm-open url events. It picks latestNavigationSpan ?? lastIdleNavSpan. After a completed navigation latestNavigationSpan is undefined, so it falls back to lastIdleNavSpan, which is never cleared and keeps pointing at the previous screen's idle span. If that span is still recording (within its 1s idle timeout), the listener tags the previous span and returns true, so setPendingDeepLink never stores the URL. The navigation actually triggered by the deep link is then created moments later and finds nothing to consume in updateLatestNavigationSpanWithCurrentRoute, leaving it untagged while the wrong span carries navigation.trigger=deeplink.

Also found at:

  • packages/core/src/js/tracing/reactnavigation.ts:250-257
  • packages/core/src/js/tracing/reactnavigation.ts:505
  • packages/core/test/tracing/reactnavigation.test.ts:495-508
Stale pending deep link leaks to the wrong navigation span when `handleLateDeepLink` tags a span - `packages/core/src/js/tracing/reactnavigation.ts:768-770`

When applyPendingDeepLinkToSpan short-circuits because the span is already in taggedDeepLinkSpans, it returns early without calling consumePendingDeepLink, leaving any previously-stored pending link in the slot. A subsequent navigation's applyPendingDeepLinkToSpan call then consumes and applies the stale link, attributing the wrong URL to an unrelated span.

Also found at:

  • packages/core/src/js/tracing/reactnavigation.ts:606

⏱ 21m 46s · 3.6M in / 211.8k out · $6.83

Annotations

Check warning on line 115 in packages/core/src/js/integrations/deeplink.ts

See this annotation in the file changed.

@sentry-warden sentry-warden / warden: find-bugs

Warm-open deep link can be consumed by a stale prior navigation span, leaving the triggered navigation span untagged

`handleLateDeepLink` in `reactnavigation.ts` is invoked synchronously for every deep link via the registered pendingDeepLink listener, including warm-open `url` events. It picks `latestNavigationSpan ?? lastIdleNavSpan`. After a completed navigation `latestNavigationSpan` is `undefined`, so it falls back to `lastIdleNavSpan`, which is never cleared and keeps pointing at the previous screen's idle span. If that span is still recording (within its 1s idle timeout), the listener tags the *previous* span and returns `true`, so `setPendingDeepLink` never stores the URL. The navigation actually triggered by the deep link is then created moments later and finds nothing to consume in `updateLatestNavigationSpanWithCurrentRoute`, leaving it untagged while the wrong span carries `navigation.trigger=deeplink`.

Check warning on line 257 in packages/core/src/js/tracing/reactnavigation.ts

See this annotation in the file changed.

@sentry-warden sentry-warden / warden: find-bugs

[FBE-RE4] Warm-open deep link can be consumed by a stale prior navigation span, leaving the triggered navigation span untagged (additional location)

`handleLateDeepLink` in `reactnavigation.ts` is invoked synchronously for every deep link via the registered pendingDeepLink listener, including warm-open `url` events. It picks `latestNavigationSpan ?? lastIdleNavSpan`. After a completed navigation `latestNavigationSpan` is `undefined`, so it falls back to `lastIdleNavSpan`, which is never cleared and keeps pointing at the previous screen's idle span. If that span is still recording (within its 1s idle timeout), the listener tags the *previous* span and returns `true`, so `setPendingDeepLink` never stores the URL. The navigation actually triggered by the deep link is then created moments later and finds nothing to consume in `updateLatestNavigationSpanWithCurrentRoute`, leaving it untagged while the wrong span carries `navigation.trigger=deeplink`.

Check warning on line 505 in packages/core/src/js/tracing/reactnavigation.ts

See this annotation in the file changed.

@sentry-warden sentry-warden / warden: find-bugs

[FBE-RE4] Warm-open deep link can be consumed by a stale prior navigation span, leaving the triggered navigation span untagged (additional location)

`handleLateDeepLink` in `reactnavigation.ts` is invoked synchronously for every deep link via the registered pendingDeepLink listener, including warm-open `url` events. It picks `latestNavigationSpan ?? lastIdleNavSpan`. After a completed navigation `latestNavigationSpan` is `undefined`, so it falls back to `lastIdleNavSpan`, which is never cleared and keeps pointing at the previous screen's idle span. If that span is still recording (within its 1s idle timeout), the listener tags the *previous* span and returns `true`, so `setPendingDeepLink` never stores the URL. The navigation actually triggered by the deep link is then created moments later and finds nothing to consume in `updateLatestNavigationSpanWithCurrentRoute`, leaving it untagged while the wrong span carries `navigation.trigger=deeplink`.

Check warning on line 508 in packages/core/test/tracing/reactnavigation.test.ts

See this annotation in the file changed.

@sentry-warden sentry-warden / warden: find-bugs

[FBE-RE4] Warm-open deep link can be consumed by a stale prior navigation span, leaving the triggered navigation span untagged (additional location)

`handleLateDeepLink` in `reactnavigation.ts` is invoked synchronously for every deep link via the registered pendingDeepLink listener, including warm-open `url` events. It picks `latestNavigationSpan ?? lastIdleNavSpan`. After a completed navigation `latestNavigationSpan` is `undefined`, so it falls back to `lastIdleNavSpan`, which is never cleared and keeps pointing at the previous screen's idle span. If that span is still recording (within its 1s idle timeout), the listener tags the *previous* span and returns `true`, so `setPendingDeepLink` never stores the URL. The navigation actually triggered by the deep link is then created moments later and finds nothing to consume in `updateLatestNavigationSpanWithCurrentRoute`, leaving it untagged while the wrong span carries `navigation.trigger=deeplink`.

Check warning on line 770 in packages/core/src/js/tracing/reactnavigation.ts

See this annotation in the file changed.

@sentry-warden sentry-warden / warden: find-bugs

Stale pending deep link leaks to the wrong navigation span when `handleLateDeepLink` tags a span

When `applyPendingDeepLinkToSpan` short-circuits because the span is already in `taggedDeepLinkSpans`, it returns early without calling `consumePendingDeepLink`, leaving any previously-stored pending link in the slot. A subsequent navigation's `applyPendingDeepLinkToSpan` call then consumes and applies the stale link, attributing the wrong URL to an unrelated span.

Check warning on line 606 in packages/core/src/js/tracing/reactnavigation.ts

See this annotation in the file changed.

@sentry-warden sentry-warden / warden: find-bugs

[4Z3-NVS] Stale pending deep link leaks to the wrong navigation span when `handleLateDeepLink` tags a span (additional location)

When `applyPendingDeepLinkToSpan` short-circuits because the span is already in `taggedDeepLinkSpans`, it returns early without calling `consumePendingDeepLink`, leaving any previously-stored pending link in the slot. A subsequent navigation's `applyPendingDeepLinkToSpan` call then consumes and applies the stale link, attributing the wrong URL to an unrelated span.