Skip to content

feat(tracing): sync JS scope propagation context to native scope - #6686

Merged
alwx merged 10 commits into
mainfrom
alwx/feature/scope-propagation
Sep 10, 2026
Merged

feat(tracing): sync JS scope propagation context to native scope#6686
alwx merged 10 commits into
mainfrom
alwx/feature/scope-propagation

Conversation

@alwx

@alwx alwx commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

📢 Type of change

  • Bugfix
  • New feature
  • Enhancement
  • Refactoring

📜 Description

Adds a new setCurrentScopePropagationContext bridge method that pushes the active JS root span's traceId, spanId, sampled, and sampleRand to the native SDK scope whenever a new root span starts. Wired up automatically via a spanStart hook in reactNativeTracingIntegration.

💡 Motivation and Context

Fixes #6237.

Native HTTP spans (OkHttp on Android, URLSession on iOS) were always orphaned — they had no knowledge of the active JS trace and ended up as standalone transactions in Sentry. After this change, native spans automatically share the same trace_id as the JS navigation transaction that triggered them.

  • JS: syncPropagationContextToNative(client) registers a spanStart hook (root spans only) and calls NATIVE.setCurrentScopePropagationContext({ traceId, spanId, sampled, sampleRand }).
  • Android: PropagationContext.fromExistingTrace(traceId, spanId, sampled, sampleRand) applied via Sentry.configureScope.
  • iOS: SentrySDK.internal.setTrace(_:spanId:) called through RNSentryInternal.swift using the existing @_spi(Private) bridge.

💚 How did you test it?

  • 5 unit tests in test/tracing/nativeScopeSync.test.ts: root span sync, child span skip, SentryNonRecordingSpan skip, sampled/sampleRand propagation, per-span firing.
  • Full test suite: 1718 tests pass.

📝 Checklist

  • I added tests to verify changes.
  • No new PII added or SDK only sends newly added PII if sendDefaultPII is enabled.
  • I updated the docs if needed.
  • I updated the wizard if needed.
  • All tests passing.
  • Public API changes reviewed by another Mobile SDK team member or implemented according to the develop docs spec.
  • No breaking changes.

🔮 Next steps

DSC/baggage propagation and iOS sampled flag — SentrySDK.internal.setTrace sets traceId+spanId only; follow up with sentry-cocoa once the API is extended.

When a new JS root span starts, push its traceId, spanId, sampled, and
sampleRand to the native SDK scope via a new bridge method. Native HTTP
instrumentation (OkHttp on Android, URLSession on iOS) then attaches the
correct sentry-trace header, linking native spans to the JS trace.

Fixes #6237

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Semver Impact of This PR

None (no version bump detected)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


  • feat(tracing): sync JS scope propagation context to native scope by alwx in #6686
  • fix(android): identify the screen appear event by name instead of class by TaduJR in #6692
  • chore(deps): update Android SDK to v8.56.0 by github-actions in #6694
  • ci: reduce sample app build matrix to production-only by alwx in #6687
  • chore(deps): update JavaScript SDK to v10.74.0 by github-actions in #6695
  • chore(deps): update Cocoa SDK to v9.28.0 by github-actions in #6693
  • test(e2e): Evaluate replay assertion on Android by antonis in #6684
  • test(e2e): Restore iOS replay assertion in captureReplay test by antonis in #6683
  • feat(visionos): Add visionOS sample app by antonis in #6676
  • fix(core): Align beforeBreadcrumb and tracesSampler error fallbacks with spec by antonis in #6675
  • chore(replay): Mark mobileReplayIntegration as stable by antonis in #6679
  • feat(sample): Add tvOS sample app by antonis in #6677
  • fix(e2e): Pin json gem < 3.0 for RN < 0.72 iOS builds by antonis in #6678
  • feat(ios): Expose enableMemoryIntrospection option by antonis in #6674
  • feat(android): Add anrProfilingSampleRate option by antonis in #6673
  • ci: Unpin Android E2E emulator build by antonis in #6672
  • ref(ios): Remove deprecated private SDK API usage by philprime in #6647
  • chore(deps): update Cocoa SDK to v9.27.0 by github-actions in #6670
  • chore(deps): update Sentry Android Gradle Plugin to v6.21.0 by github-actions in #6671
  • chore(deps): Bump fast-uri to ^3.1.6 to resolve security alerts by antonis in #6662
  • chore(deps): Bump qs to ^6.16.0 to resolve security alerts by antonis in #6663
  • chore(deps): Bump @xmldom/xmldom pins to patched versions to resolve security alerts by antonis in #6664
  • docs: Revamp root and nested AGENTS.md by antonis in #6666

🤖 This preview updates automatically when you update the PR.

@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor
Messages
📖 Do not forget to update Sentry-docs with your feature once the pull request gets approved.

Generated by 🚫 dangerJS against dbcdc7d

alwx and others added 2 commits September 8, 2026 14:42
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@alwx
alwx marked this pull request as ready for review September 8, 2026 12:45
Comment thread packages/core/android/src/main/java/io/sentry/react/RNSentryModuleImpl.java Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread packages/core/src/js/tracing/span.ts
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Comment thread packages/core/ios/RNSentryInternal.swift
Comment thread packages/core/android/src/main/java/io/sentry/react/RNSentryModuleImpl.java Outdated
Comment thread packages/core/src/js/tracing/reactnativetracing.ts
Comment thread packages/core/android/src/main/java/io/sentry/react/RNSentryModuleImpl.java Outdated
…ridge modules

- Add missing @ReactMethod wrapper in oldarch RNSentryModule
- Add missing @OverRide wrapper in newarch RNSentryModule
- Guard against null ctx/traceId/spanId in RNSentryModuleImpl
- Pass null instead of sampled boolean as sample rate; the Java SDK
  expects the actual configured rate (0.0–1.0), not a boolean flag

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Comment thread packages/core/ios/RNSentryInternal.swift
- Update native context for all root spans including non-recording (unsampled)
  ones to prevent stale traceId lingering from a previous navigation
- Make setCurrentScopePropagationContext synchronous on iOS (RCT_EXPORT_SYNCHRONOUS_TYPED_METHOD)
  and Android (boolean return) to eliminate race with native URLSession/OkHttp
- Update NativeRNSentry spec return type void→boolean to match synchronous codegen path
- Document that iOS setTrace does not accept sampled/sampleRand (Cocoa SDK limitation)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit ae93384. Configure here.

Comment thread packages/core/ios/RNSentryInternal.swift
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@alwx
alwx requested a review from a team as a code owner September 9, 2026 08:59
Comment thread packages/core/src/js/tracing/span.ts
Comment thread packages/core/src/js/tracing/span.ts
Comment thread packages/core/ios/RNSentryInternal.swift
…ive scope sync

- RNSentryInternal: reformat 32-char hex traceId to hyphenated UUID before
  passing to SentryId(uuidString:); without hyphens it silently produces an
  empty SentryId and trace linking breaks on iOS.

- syncPropagationContextToNative: skip root spans that are not the active
  span so inactive forceTransaction roots (app start, expo-updates) do not
  clobber the native propagation context of an in-flight navigation span.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Comment thread packages/core/src/js/tracing/span.ts
Comment thread packages/core/android/src/main/java/io/sentry/react/RNSentryModuleImpl.java Outdated
…sync

- span.ts: revert getActiveSpan() guard — spanStart fires before idle spans
  are made active, so the guard was skipping every navigation span and the
  feature never synced. All root spans now trigger a sync; the JSDoc explains
  the timing constraint.

- RNSentryModuleImpl: read the JS `sampled` boolean and apply it via
  PropagationContext.setSampled() so OkHttp spans carry the correct
  isSampled decision, not just sampleRand.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Comment on lines +223 to +229
const ctx = span.spanContext();
const propagationCtx = getCurrentScope().getPropagationContext();
NATIVE.setCurrentScopePropagationContext({
traceId: ctx.traceId,
spanId: ctx.spanId,
sampled: spanIsSampled(span),
sampleRand: propagationCtx.sampleRand ?? Math.random(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Root spans created without startIdleSpan can cause a mismatched propagation context, combining a new traceId with a stale sampleRand, leading to incorrect native sampling.
Severity: MEDIUM

Suggested Fix

Ensure that code paths creating root spans, like startInactiveSpan({ forceTransaction: true }), explicitly update the propagation context with a new traceId and sampleRand before the span starts, similar to how startIdleSpan does. This will prevent syncPropagationContextToNative from using stale data.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: packages/core/src/js/tracing/span.ts#L223-L229

Potential issue: For root spans not created via `startIdleSpan`, such as those for
app-start using `startInactiveSpan({ forceTransaction: true })`, the native scope
synchronization logic can create a mismatched propagation context. When
`syncPropagationContextToNative` is triggered, it uses the `traceId` from the new span
but may use a stale `sampleRand` value from the previous trace's scope. This sends a
mismatched context to the native layer, potentially causing incorrect sampling decisions
for downstream native HTTP calls.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks valid 👍
Also these background roots come from startInactiveSpan({ forceTransaction: true }) (appStart.ts:556/1019, expoupdateslistener.ts:207/224) and can start mid navigation, overwriting the native trace that in-flight native HTTP spans link to, with no restore when they end. Refreshing their propagation context (the suggested fix) keeps the pushed values consistent but still links native calls to the wrong trace.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The right fix would be to skip background/inactive roots (those created via startInactiveSpan that never become the active span). But distinguishing those from navigation roots at spanStart time is tricky so I will rather create a follow-up issue.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good 👍 Thank you 🙇

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alwx
alwx requested a review from antonis September 10, 2026 07:51
@antonis antonis added the ready-to-merge Triggers the full CI test suite label Sep 10, 2026
}

@ReactMethod
@ReactMethod(isBlockingSynchronousMethod = true)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

q: Why do we change this? I think it might degrade the performance

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will do

@github-actions

Copy link
Copy Markdown
Contributor

Android (legacy) Performance metrics 🚀

  Plain With Sentry Diff
Startup time 485.26 ms 536.90 ms 51.63 ms
Size 50.56 MiB 56.47 MiB 5.91 MiB

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
ae37560+dirty 470.40 ms 564.12 ms 93.72 ms
40c9884+dirty 430.40 ms 497.76 ms 67.36 ms
7ff4d0f+dirty 413.81 ms 450.64 ms 36.83 ms
580fb5c+dirty 436.34 ms 471.63 ms 35.28 ms
a5d243c+dirty 424.52 ms 485.18 ms 60.66 ms
4b87b12+dirty 421.82 ms 413.60 ms -8.22 ms
bc8f61e+dirty 442.98 ms 506.56 ms 63.58 ms
774257e+dirty 407.09 ms 447.46 ms 40.37 ms
5257d80+dirty 423.37 ms 467.54 ms 44.17 ms
3817909+dirty 406.67 ms 416.58 ms 9.91 ms

App size

Revision Plain With Sentry Diff
ae37560+dirty 48.30 MiB 53.60 MiB 5.29 MiB
40c9884+dirty 49.74 MiB 55.09 MiB 5.34 MiB
7ff4d0f+dirty 48.30 MiB 53.60 MiB 5.30 MiB
580fb5c+dirty 49.74 MiB 54.79 MiB 5.05 MiB
a5d243c+dirty 48.30 MiB 53.54 MiB 5.23 MiB
4b87b12+dirty 43.75 MiB 48.14 MiB 4.39 MiB
bc8f61e+dirty 49.74 MiB 55.09 MiB 5.35 MiB
774257e+dirty 48.30 MiB 53.58 MiB 5.28 MiB
5257d80+dirty 48.30 MiB 53.58 MiB 5.28 MiB
3817909+dirty 43.75 MiB 48.08 MiB 4.33 MiB

@sentry

sentry Bot commented Sep 10, 2026

Copy link
Copy Markdown

📲 Install Builds

Android

🔗 App Name App ID Version Configuration
Sentry RN io.sentry.reactnative.sample 8.25.0 (105) Release

⚙️ sentry-react-native Build Distribution Settings

@github-actions

Copy link
Copy Markdown
Contributor

iOS (legacy) Performance metrics 🚀

  Plain With Sentry Diff
Startup time 3850.53 ms 1215.11 ms -2635.42 ms
Size 5.15 MiB 6.89 MiB 1.74 MiB

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
d200634+dirty 3829.38 ms 1224.08 ms -2605.30 ms
3b6e9f9+dirty 3851.90 ms 1233.33 ms -2618.57 ms
4e0b819+dirty 3839.05 ms 1210.75 ms -2628.30 ms
1e5d96d+dirty 3851.45 ms 1212.05 ms -2639.41 ms
0a9e622+dirty 3835.87 ms 1221.07 ms -2614.80 ms
7ac3378+dirty 1213.37 ms 1218.15 ms 4.78 ms
9ad4522+dirty 3850.40 ms 1222.40 ms -2628.00 ms
20fbd51+dirty 3827.47 ms 1213.32 ms -2614.15 ms
d0e3b3e+dirty 3854.77 ms 1218.58 ms -2636.19 ms
822d35b+dirty 3830.64 ms 1209.20 ms -2621.44 ms

App size

Revision Plain With Sentry Diff
d200634+dirty 5.15 MiB 6.89 MiB 1.74 MiB
3b6e9f9+dirty 5.15 MiB 6.68 MiB 1.53 MiB
4e0b819+dirty 4.98 MiB 6.46 MiB 1.49 MiB
1e5d96d+dirty 4.98 MiB 6.46 MiB 1.49 MiB
0a9e622+dirty 4.98 MiB 6.51 MiB 1.53 MiB
7ac3378+dirty 3.38 MiB 4.76 MiB 1.38 MiB
9ad4522+dirty 4.98 MiB 6.56 MiB 1.58 MiB
20fbd51+dirty 4.98 MiB 6.46 MiB 1.49 MiB
d0e3b3e+dirty 4.98 MiB 6.51 MiB 1.53 MiB
822d35b+dirty 4.98 MiB 6.50 MiB 1.53 MiB

@github-actions

Copy link
Copy Markdown
Contributor

iOS (new) Performance metrics 🚀

  Plain With Sentry Diff
Startup time 3862.89 ms 1238.78 ms -2624.12 ms
Size 5.15 MiB 6.89 MiB 1.74 MiB

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
68ae91b+dirty 3836.36 ms 1222.04 ms -2614.32 ms
0b5a379+dirty 3857.69 ms 1230.34 ms -2627.35 ms
bf168a4+dirty 3846.83 ms 1221.33 ms -2625.49 ms
f170ec3+dirty 3844.74 ms 1222.67 ms -2622.07 ms
68672fc+dirty 3832.22 ms 1228.29 ms -2603.93 ms
84cba31+dirty 3830.40 ms 1204.44 ms -2625.96 ms
2c735cc+dirty 1223.33 ms 1224.38 ms 1.04 ms
b9bebee+dirty 3858.02 ms 1231.92 ms -2626.11 ms
a50b33d+dirty 1207.11 ms 1212.10 ms 5.00 ms
5569641+dirty 3824.35 ms 1210.78 ms -2613.57 ms

App size

Revision Plain With Sentry Diff
68ae91b+dirty 4.98 MiB 6.46 MiB 1.48 MiB
0b5a379+dirty 5.15 MiB 6.70 MiB 1.54 MiB
bf168a4+dirty 4.98 MiB 6.47 MiB 1.49 MiB
f170ec3+dirty 5.15 MiB 6.69 MiB 1.53 MiB
68672fc+dirty 5.15 MiB 6.71 MiB 1.55 MiB
84cba31+dirty 4.98 MiB 6.55 MiB 1.57 MiB
2c735cc+dirty 3.38 MiB 4.74 MiB 1.35 MiB
b9bebee+dirty 5.15 MiB 6.68 MiB 1.53 MiB
a50b33d+dirty 3.38 MiB 4.73 MiB 1.35 MiB
5569641+dirty 5.15 MiB 6.67 MiB 1.51 MiB

@antonis antonis left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left a couple of comments that should be checked but nothing blocking on my side 👍

@github-actions

Copy link
Copy Markdown
Contributor

Android (new) Performance metrics 🚀

  Plain With Sentry Diff
Startup time 426.06 ms 431.41 ms 5.35 ms
Size 50.56 MiB 56.47 MiB 5.91 MiB

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
f3215d3+dirty 396.53 ms 436.66 ms 40.13 ms
d0e3b3e+dirty 443.19 ms 480.00 ms 36.81 ms
a5d243c+dirty 499.20 ms 525.62 ms 26.42 ms
7fd0012+dirty 422.41 ms 445.57 ms 23.17 ms
0a147b2+dirty 442.80 ms 522.24 ms 79.44 ms
d7d54c6+dirty 423.54 ms 498.56 ms 75.02 ms
c823bb5+dirty 468.26 ms 516.16 ms 47.90 ms
5789645+dirty 502.66 ms 594.19 ms 91.53 ms
40c9884+dirty 453.76 ms 478.98 ms 25.22 ms
d771cd5+dirty 432.29 ms 464.38 ms 32.09 ms

App size

Revision Plain With Sentry Diff
f3215d3+dirty 48.30 MiB 53.49 MiB 5.19 MiB
d0e3b3e+dirty 49.74 MiB 55.09 MiB 5.34 MiB
a5d243c+dirty 48.30 MiB 53.54 MiB 5.23 MiB
7fd0012+dirty 50.56 MiB 56.46 MiB 5.90 MiB
0a147b2+dirty 49.74 MiB 55.08 MiB 5.34 MiB
d7d54c6+dirty 49.74 MiB 55.09 MiB 5.34 MiB
c823bb5+dirty 48.30 MiB 53.58 MiB 5.28 MiB
5789645+dirty 49.74 MiB 54.85 MiB 5.11 MiB
40c9884+dirty 49.74 MiB 55.09 MiB 5.34 MiB
d771cd5+dirty 49.74 MiB 55.36 MiB 5.62 MiB

JS callers declare it void and never use the return value, so blocking
the JS thread was unnecessary overhead.
@alwx
alwx merged commit 60ad791 into main Sep 10, 2026
71 of 83 checks passed
@alwx
alwx deleted the alwx/feature/scope-propagation branch September 10, 2026 10:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-to-merge Triggers the full CI test suite

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Sync JS scope propagation context to native scope (link native spans to JS trace)

2 participants