Skip to content

feat(core): Add refs field to Scope for non-serialized references - #23236

Merged
mydea merged 4 commits into
developfrom
feat/scope-refs-field
Aug 11, 2026
Merged

feat(core): Add refs field to Scope for non-serialized references#23236
mydea merged 4 commits into
developfrom
feat/scope-refs-field

Conversation

@mydea

@mydea mydea commented Aug 10, 2026

Copy link
Copy Markdown
Member

Adds a refs field to Scope — a shared bag for object references that are associated with a scope but must not be serialized (the active span in core, the OpenTelemetry Context in the opentelemetry package). refs is cloned as-is (shallow) in Scope.clone().

Root cause

The active span and the OTel context were stored in two separate, ad-hoc non-enumerable fields (_sentrySpan and _scopeContext). Scope.clone() only knew about the core span field and explicitly re-set it on the clone; it had no knowledge of the opentelemetry-owned _scopeContext. As a result, cloning a scope under OTel dropped the context reference, so getActiveSpan(clonedScope) — which reads the span out of the context attached to the scope — resolved to undefined. The two implementations disagreed on whether a clone preserved the active span.

Consolidating both references into a single refs object that clone() copies wholesale removes that asymmetry: a cloned scope now retains its span (core) and its context (OTel) uniformly.

This also allows us to remove some span/scope specifics from the scope class to keep this more generic. the scope class will simply clone/reset refs, whatever they may be.

Notable decisions:

  • refs is stored as a non-enumerable property (via addNonEnumerableProperty), matching the previous behavior of _sentrySpan/_scopeContext. This keeps it out of toEqual, Object.keys, toJSON, and event serialization — an enumerable field would otherwise leak the active span into structural comparisons and serialized payloads.
  • clone() does a shallow spread ({ ...this.refs }), consistent with how the other reference-holding scope fields are copied.
  • clear() resets refs to {}, replacing the previous per-field span reset.

The existing MaybeWeakRef wrapping is preserved for both the span and the context, so the GC/circular-reference protections are unchanged.

@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 3 potential issues.

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 58d67a2. Configure here.

Comment thread packages/opentelemetry/test/utils/contextData.test.ts
Comment thread packages/core/src/scope.ts Outdated
Comment thread packages/opentelemetry/test/utils/contextData.test.ts
@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

Path Size % Change Change
@sentry/browser 30.26 kB -0.14% -41 B 🔽
@sentry/browser - with treeshaking flags 28.44 kB -0.15% -40 B 🔽
@sentry/browser - with treeshaking flags tracing without tracing 26.78 kB -0.12% -31 B 🔽
@sentry/browser (incl. Tracing) 48.57 kB +0.04% +18 B 🔺
@sentry/browser (incl. Tracing + Span Streaming) 48.58 kB +0.05% +24 B 🔺
@sentry/browser (incl. Tracing, Profiling) 53.41 kB +0.03% +14 B 🔺
@sentry/browser (incl. Tracing, Replay) 88 kB +0.03% +21 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 77.38 kB -0.01% -5 B 🔽
@sentry/browser (incl. Tracing, Replay with Canvas) 92.71 kB +0.01% +6 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback) 105.39 kB +0.02% +15 B 🔺
@sentry/browser (incl. Feedback) 47.58 kB -0.14% -62 B 🔽
@sentry/browser (incl. sendFeedback) 35.08 kB -0.14% -47 B 🔽
@sentry/browser (incl. FeedbackAsync) 40.23 kB -0.14% -56 B 🔽
@sentry/browser (incl. Metrics) 31.29 kB -0.13% -40 B 🔽
@sentry/browser (incl. Logs) 31.55 kB -0.09% -26 B 🔽
@sentry/browser (incl. Metrics & Logs) 32.24 kB -0.13% -39 B 🔽
@sentry/react 32.06 kB -0.13% -40 B 🔽
@sentry/react (incl. Tracing) 50.78 kB +0.06% +28 B 🔺
@sentry/vue 35.41 kB +0.07% +22 B 🔺
@sentry/vue (incl. Tracing) 50.54 kB +0.04% +17 B 🔺
@sentry/svelte 30.28 kB -0.17% -50 B 🔽
CDN Bundle 31.52 kB -0.09% -27 B 🔽
CDN Bundle (incl. Tracing) 48.86 kB -0.02% -6 B 🔽
CDN Bundle (incl. Logs, Metrics) 33.75 kB -0.09% -29 B 🔽
CDN Bundle (incl. Tracing, Logs, Metrics) 50.83 kB -0.03% -11 B 🔽
CDN Bundle (incl. Replay, Logs, Metrics) 74.3 kB -0.04% -26 B 🔽
CDN Bundle (incl. Tracing, Replay) 86.46 kB +0.01% +8 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 88.35 kB +0.01% +2 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) 92.17 kB +0.01% +2 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 94.17 kB +0.03% +28 B 🔺
CDN Bundle - uncompressed 93.59 kB -0.11% -95 B 🔽
CDN Bundle (incl. Tracing) - uncompressed 146.68 kB +0.02% +26 B 🔺
CDN Bundle (incl. Logs, Metrics) - uncompressed 100.06 kB -0.1% -95 B 🔽
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 152.54 kB +0.02% +26 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 228.98 kB -0.05% -95 B 🔽
CDN Bundle (incl. Tracing, Replay) - uncompressed 265.93 kB +0.01% +26 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 271.77 kB +0.01% +26 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 279.63 kB +0.01% +26 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 285.46 kB +0.01% +26 B 🔺
@sentry/nextjs (client) 53.3 kB -0.02% -8 B 🔽
@sentry/sveltekit (client) 48.98 kB +0.04% +15 B 🔺
@sentry/core/server 65.58 kB +0.03% +14 B 🔺
@sentry/core/browser 51.9 kB +0.02% +8 B 🔺
@sentry/node 118.36 kB - -
@sentry/node/import (ESM hook with diagnostics-channel injection) 0 B added added
@sentry/node - without tracing 82.5 kB +0.02% +10 B 🔺
@sentry/aws-serverless 91.86 kB +0.02% +14 B 🔺
@sentry/cloudflare (withSentry) - minified 214.33 kB +0.02% +31 B 🔺
@sentry/cloudflare (withSentry) 529.69 kB +0.01% +13 B 🔺

View base workflow run

@mydea
mydea marked this pull request as ready for review August 10, 2026 13:52
@mydea
mydea requested a review from a team as a code owner August 10, 2026 13:52
@mydea
mydea requested review from JPeer264, Lms24 and isaacs and removed request for a team August 10, 2026 13:52
@mydea
mydea force-pushed the feat/scope-refs-field branch from e7ed716 to 2f7f68a Compare August 11, 2026 07:15
mydea and others added 4 commits August 11, 2026 09:52
Store the active span (core) and OpenTelemetry context (opentelemetry) in a
shared, non-enumerable `refs` bag on the scope, and clone it in `clone()`.

This makes the span/context references survive `scope.clone()` consistently
across both the core and OpenTelemetry implementations. Previously the OTel
context lived in a separate `_scopeContext` field that `clone()` did not copy,
so `getActiveSpan(clonedScope)` returned `undefined` under OTel.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mydea
mydea force-pushed the feat/scope-refs-field branch from 2f7f68a to bf0f4f2 Compare August 11, 2026 07:53
@mydea
mydea merged commit 2e52ead into develop Aug 11, 2026
273 checks passed
@mydea
mydea deleted the feat/scope-refs-field branch August 11, 2026 08:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants