feat(core)!: Make beforeSendSpan compatible with streamed spans by default - #22643

Merged
Lms24 merged 13 commits into
developfrom
feat/before-send-span-streamed-default
Aug 4, 2026
Merged

feat(core)!: Make beforeSendSpan compatible with streamed spans by default#22643
Lms24 merged 13 commits into
developfrom
feat/before-send-span-streamed-default

Conversation

@Lms24

@Lms24Lms24 commented Jul 24, 2026

Copy link
Copy Markdown
Member

This PR changes beforeSendSpan to receive StreamedSpanJSON by default, matching the new trace lifecycle default.

Callbacks that intentionally process legacy transaction span JSON need to add the withStaticSpan wrapper that marks the callback as "static"-compatible and hands users the SpanJSON type they used in the callback beforehand.

The withStreamedSpanwrapper remains available as a deprecated compatibility helper and is scheduled for removal in version 12.

More changes:

  • invalid beforeSendSpan callbacks no longer lead to switching the traceLifecycle. Since it now has a default and users need to actively opt out of span streaming, I think it's fair to treat incompatible callbacks as "invalid" and hence skip over them.
  • The beforeSendSpan compatibility checks were moved from the integrations into the core client which ensures that they always run now, even if users selected the static life cycle and hence spanStreamingIntegration doesn't get added.
  • Because we removed sending INP spans as v1 (SpanJSON) spans in favour of always sending them as v2 spans, we now convert a StreamedSpanJson to SpanJson in captureSpan, hand it to the static callback and then convert it back. Not great but I think we need to let users still scrub INP spans.

Closes#22349

@github-actions

github-actionsBot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser29.83 kB+0.13%+37 B 🔺
@sentry/browser - with treeshaking flags28.03 kB+0.05%+14 B 🔺
@sentry/browser (incl. Tracing)47.25 kB+0.09%+42 B 🔺
@sentry/browser (incl. Tracing + Span Streaming)47.26 kB+0.1%+44 B 🔺
@sentry/browser (incl. Tracing, Profiling)51.98 kB+0.09%+43 B 🔺
@sentry/browser (incl. Tracing, Replay)86.58 kB+0.08%+61 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags76 kB+0.06%+42 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas)91.27 kB+0.04%+28 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback)103.92 kB+0.05%+42 B 🔺
@sentry/browser (incl. Feedback)47.13 kB+0.08%+33 B 🔺
@sentry/browser (incl. sendFeedback)34.67 kB+0.08%+27 B 🔺
@sentry/browser (incl. FeedbackAsync)39.78 kB+0.08%+30 B 🔺
@sentry/browser (incl. Metrics)30.91 kB+0.14%+41 B 🔺
@sentry/browser (incl. Logs)31.14 kB+0.13%+39 B 🔺
@sentry/browser (incl. Metrics & Logs)31.81 kB+0.12%+38 B 🔺
@sentry/react31.63 kB+0.12%+36 B 🔺
@sentry/react (incl. Tracing)49.51 kB+0.14%+69 B 🔺
@sentry/vue34.92 kB+0.17%+57 B 🔺
@sentry/vue (incl. Tracing)49.23 kB+0.1%+46 B 🔺
@sentry/svelte29.86 kB+0.13%+36 B 🔺
CDN Bundle31.87 kB+0.01%+1 B 🔺
CDN Bundle (incl. Tracing)47.52 kB+0.01%+1 B 🔺
CDN Bundle (incl. Logs, Metrics)33.41 kB+0.02%+4 B 🔺
CDN Bundle (incl. Tracing, Logs, Metrics)48.93 kB-0.02%-6 B 🔽
CDN Bundle (incl. Replay, Logs, Metrics)72.75 kB-0.01%-2 B 🔽
CDN Bundle (incl. Tracing, Replay)85.18 kB-0.01%-8 B 🔽
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)86.49 kB-0.01%-5 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback)91 kB-0.01%-9 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)92.31 kB-0.02%-10 B 🔽
CDN Bundle - uncompressed94.62 kB-0.09%-84 B 🔽
CDN Bundle (incl. Tracing) - uncompressed142.1 kB-0.05%-57 B 🔽
CDN Bundle (incl. Logs, Metrics) - uncompressed99.24 kB-0.09%-84 B 🔽
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed146.08 kB-0.04%-57 B 🔽
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed223.94 kB-0.04%-84 B 🔽
CDN Bundle (incl. Tracing, Replay) - uncompressed261.36 kB-0.03%-57 B 🔽
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed265.32 kB-0.03%-57 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed275.06 kB-0.03%-57 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed279.01 kB-0.03%-57 B 🔽
@sentry/nextjs (client)52.07 kB+0.12%+61 B 🔺
@sentry/sveltekit (client)47.7 kB+0.14%+62 B 🔺
@sentry/core/server65.52 kB+0.04%+23 B 🔺
@sentry/core/browser51.88 kB+0.09%+43 B 🔺
@sentry/node120.24 kB+0.05%+49 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection)0 Baddedadded
@sentry/node - without tracing83.69 kB+0.06%+50 B 🔺
@sentry/aws-serverless92.35 kB+0.06%+51 B 🔺
@sentry/cloudflare (withSentry) - minified218.65 kB+0.01%+9 B 🔺
@sentry/cloudflare (withSentry)538.85 kB+0.04%+196 B 🔺

View base workflow run

@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from a32a10f to bec33b0CompareJuly 27, 2026 08:41
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from bec33b0 to e303625CompareJuly 27, 2026 14:58
Comment threadpackages/core/src/client.ts
Base automatically changed from feat/span-streaming-default to developJuly 28, 2026 07:36
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch 2 times, most recently from a6f845e to 344055eCompareJuly 28, 2026 08:22
@Lms24Lms24 changed the title feat(core)!: Stream beforeSendSpan payloads by defaultfeat(core)!: Make beforeSendSpan compatible with streamed spans by defaultJul 28, 2026
@Lms24Lms24 self-assigned this Jul 28, 2026
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch 2 times, most recently from 9f59f8c to 9176c4bCompareJuly 29, 2026 15:37
@Lms24
Lms24 marked this pull request as ready for review July 29, 2026 15:42
@Lms24
Lms24 requested review from a team as code ownersJuly 29, 2026 15:42
@Lms24
Lms24 requested review from isaacs, msonnb, mydea, nicohrubec and s1gr1d and removed request for a teamJuly 29, 2026 15:42

@isaacsisaacs left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm not super familiar with this part of the machinery, so leaving it as a comment. If I'm misreading things or someone else disagrees, I'm happy to be overruled :)

Since span streaming is the direction we're moving, and the goal is to make it the default, moving these checks out of the integration and into the client feels like the right call.

Comment threadpackages/core/src/client.ts
Comment threadpackages/core/src/client.ts
Comment threadpackages/core/src/tracing/spans/spanJsonToStreamedSpan.ts Outdated
Comment threadpackages/core/src/tracing/spans/spanJsonToStreamedSpan.ts Outdated
Comment threadpackages/core/src/types/options.ts Outdated
Comment threadpackages/core/test/lib/tracing/spans/captureSpan.test.ts Outdated
Comment threadpackages/core/test/lib/tracing/sentrySpan.test.ts Outdated
Comment threadpackages/core/test/lib/tracing/spans/spanJsonToStreamedSpan.test.ts Outdated
Comment threadMIGRATION.md
Comment threadpackages/core/test/lib/tracing/sentrySpan.test.ts Outdated
Comment threadpackages/core/test/lib/client.test.ts
Lms24 added a commit that referenced this pull request Jul 31, 2026
…` and log warnings if used with streaming (#22908)
This PR:
1. Deprecates `beforeSendTransaction` and `ignoreTransactions`
2. Logs a console warning (intentionally not gated with `debug: true`)
to warn users if they use any of the two options with span streaming
enabled
I intentionally didn't update the migration guide. this is handled in
#22643.
more details in
#22856closes#22856closes#20279
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from 0ec024e to 4910062CompareAugust 3, 2026 12:48
Comment threadpackages/core/src/tracing/sentrySpan.ts Outdated
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from c3ecf63 to 0749d71CompareAugust 3, 2026 14:31
@Lms24
Lms24 marked this pull request as ready for review August 3, 2026 14:48

@chargomechargome left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM just two questions!

Comment threadpackages/browser/src/integrations/spanstreaming.ts
Comment threadpackages/core/src/tracing/spans/captureSpan.ts Outdated
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from 9ef7183 to f6cfc05CompareAugust 4, 2026 09:23

@cursorcursorBot 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 f6cfc05. Configure here.

Comment threadpackages/core/test/lib/tracing/spans/captureSpan.test.ts
Comment threadpackages/core/src/client.ts
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from 9dcfb44 to 6fb09a1CompareAugust 4, 2026 13:33
Lms24and others added 13 commits August 4, 2026 15:47
Make streamed span JSON the default beforeSendSpan contract and provide
withStaticSpan for callbacks that still process transaction span JSON.
Deprecate withStreamedSpan for removal in version 12.
BREAKING CHANGE: beforeSendSpan receives StreamedSpanJSON by default.
Fixes#22349
Co-Authored-By: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
`withStreamedSpan` no longer marks its callback. Nothing reads `_streamed` since
`isStreamedBeforeSendSpanCallback` became "not wrapped with `withStaticSpan`", so the
wrapper returns the callback unchanged instead of mutating a function it was handed.
Resolve `traceLifecycle` once in the `Client` constructor, where any value other than
`'static'` normalizes to the `'stream'` default. Neither span streaming integration
writes back to the option anymore, which removes an ordering hazard: integrations that
read `hasSpanStreamingEnabled` during their own `setup` could observe the value before
or after the mutation depending on integration order. The registration gates now test
`!== 'static'` so they agree with the normalized value; previously an unknown value
failed the gate but still resolved to `'stream'`, leaving nothing to flush spans.
Move the `beforeSendSpan` format check into the constructor as well. A callback is only
invoked for the span format matching the trace lifecycle, so a mismatch means it is
never called. The client can warn where the integration could not: with
`traceLifecycle: 'static'` the streaming integration is never registered, so an
unwrapped callback previously went unreported.
A `withStaticSpan` callback under `traceLifecycle: 'stream'` no longer downgrades the
lifecycle to `'static'`. Opting out of streaming requires setting
`traceLifecycle: 'static'` explicitly.
Refs #22349
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from 9810912 to 4d63933CompareAugust 4, 2026 13:47
Comment on lines 79 to +82
const processedSpan =
beforeSendSpan && isStreamedBeforeSendSpanCallback(beforeSendSpan)
// check for traceLifecycle here because in static lifecycle,
// captureSpan is called for INP spans. If an unmigrated beforeSendSpan
// callback is run on these spans, it will throw an error.

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.

Bug: In production builds, old beforeSendSpan callbacks expecting v1 SpanJSON format are silently invoked with the new v2 StreamedSpanJSON format, leading to incorrect span processing.
Severity: HIGH

Suggested Fix

Implement a runtime check within captureSpan to prevent v1-style beforeSendSpan callbacks from being invoked with v2 StreamedSpanJSON data in production. Either skip these incompatible callbacks or throw an error to make the breaking change explicit, rather than allowing silent data corruption. The current DEBUG_BUILD warnings are insufficient as they don't run in production.

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/tracing/spans/captureSpan.ts#L79-L82
Potential issue: With the new default `traceLifecycle: 'stream'`, the `captureSpan`
function now invokes `beforeSendSpan` callbacks that are not wrapped with
`withStaticSpan`. If a user has an older callback designed for the v1 `SpanJSON` format
(expecting fields like `description`), it will now be called with the v2
`StreamedSpanJSON` format (with fields like `name` and `end_timestamp`). This mismatch
will cause the callback to silently process incorrect data, leading to failed or
improper span modification. While development builds have warnings, these are stripped
from production builds, making this a silent failure for users who update.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

this is somewhat wrong because we already warn about this and users' callbacks will "break" if they type-check. Another thing we'll fix in a follow-up is to try/catch beforeSendSpan callback invocations, so that the SDK doesn't crash users.

@Lms24
Lms24 merged commit 5e76abe into developAug 4, 2026
266 checks passed
@Lms24
Lms24 deleted the feat/before-send-span-streamed-default branch August 4, 2026 15:34
Sign up for freeto 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.

Make streamed span types the default in public APIs

3 participants

@Lms24@isaacs@chargome
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

feat(core)!: Make beforeSendSpan compatible with streamed spans by default - #22643

Merged
Lms24 merged 13 commits into
developfrom
feat/before-send-span-streamed-default
Aug 4, 2026
Merged

feat(core)!: Make beforeSendSpan compatible with streamed spans by default#22643
Lms24 merged 13 commits into
developfrom
feat/before-send-span-streamed-default

Conversation

@Lms24

@Lms24Lms24 commented Jul 24, 2026

Copy link
Copy Markdown
Member

This PR changes beforeSendSpan to receive StreamedSpanJSON by default, matching the new trace lifecycle default.

Callbacks that intentionally process legacy transaction span JSON need to add the withStaticSpan wrapper that marks the callback as "static"-compatible and hands users the SpanJSON type they used in the callback beforehand.

The withStreamedSpanwrapper remains available as a deprecated compatibility helper and is scheduled for removal in version 12.

More changes:

  • invalid beforeSendSpan callbacks no longer lead to switching the traceLifecycle. Since it now has a default and users need to actively opt out of span streaming, I think it's fair to treat incompatible callbacks as "invalid" and hence skip over them.
  • The beforeSendSpan compatibility checks were moved from the integrations into the core client which ensures that they always run now, even if users selected the static life cycle and hence spanStreamingIntegration doesn't get added.
  • Because we removed sending INP spans as v1 (SpanJSON) spans in favour of always sending them as v2 spans, we now convert a StreamedSpanJson to SpanJson in captureSpan, hand it to the static callback and then convert it back. Not great but I think we need to let users still scrub INP spans.

Closes#22349

@github-actions

github-actionsBot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser29.83 kB+0.13%+37 B 🔺
@sentry/browser - with treeshaking flags28.03 kB+0.05%+14 B 🔺
@sentry/browser (incl. Tracing)47.25 kB+0.09%+42 B 🔺
@sentry/browser (incl. Tracing + Span Streaming)47.26 kB+0.1%+44 B 🔺
@sentry/browser (incl. Tracing, Profiling)51.98 kB+0.09%+43 B 🔺
@sentry/browser (incl. Tracing, Replay)86.58 kB+0.08%+61 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags76 kB+0.06%+42 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas)91.27 kB+0.04%+28 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback)103.92 kB+0.05%+42 B 🔺
@sentry/browser (incl. Feedback)47.13 kB+0.08%+33 B 🔺
@sentry/browser (incl. sendFeedback)34.67 kB+0.08%+27 B 🔺
@sentry/browser (incl. FeedbackAsync)39.78 kB+0.08%+30 B 🔺
@sentry/browser (incl. Metrics)30.91 kB+0.14%+41 B 🔺
@sentry/browser (incl. Logs)31.14 kB+0.13%+39 B 🔺
@sentry/browser (incl. Metrics & Logs)31.81 kB+0.12%+38 B 🔺
@sentry/react31.63 kB+0.12%+36 B 🔺
@sentry/react (incl. Tracing)49.51 kB+0.14%+69 B 🔺
@sentry/vue34.92 kB+0.17%+57 B 🔺
@sentry/vue (incl. Tracing)49.23 kB+0.1%+46 B 🔺
@sentry/svelte29.86 kB+0.13%+36 B 🔺
CDN Bundle31.87 kB+0.01%+1 B 🔺
CDN Bundle (incl. Tracing)47.52 kB+0.01%+1 B 🔺
CDN Bundle (incl. Logs, Metrics)33.41 kB+0.02%+4 B 🔺
CDN Bundle (incl. Tracing, Logs, Metrics)48.93 kB-0.02%-6 B 🔽
CDN Bundle (incl. Replay, Logs, Metrics)72.75 kB-0.01%-2 B 🔽
CDN Bundle (incl. Tracing, Replay)85.18 kB-0.01%-8 B 🔽
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)86.49 kB-0.01%-5 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback)91 kB-0.01%-9 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)92.31 kB-0.02%-10 B 🔽
CDN Bundle - uncompressed94.62 kB-0.09%-84 B 🔽
CDN Bundle (incl. Tracing) - uncompressed142.1 kB-0.05%-57 B 🔽
CDN Bundle (incl. Logs, Metrics) - uncompressed99.24 kB-0.09%-84 B 🔽
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed146.08 kB-0.04%-57 B 🔽
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed223.94 kB-0.04%-84 B 🔽
CDN Bundle (incl. Tracing, Replay) - uncompressed261.36 kB-0.03%-57 B 🔽
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed265.32 kB-0.03%-57 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed275.06 kB-0.03%-57 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed279.01 kB-0.03%-57 B 🔽
@sentry/nextjs (client)52.07 kB+0.12%+61 B 🔺
@sentry/sveltekit (client)47.7 kB+0.14%+62 B 🔺
@sentry/core/server65.52 kB+0.04%+23 B 🔺
@sentry/core/browser51.88 kB+0.09%+43 B 🔺
@sentry/node120.24 kB+0.05%+49 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection)0 Baddedadded
@sentry/node - without tracing83.69 kB+0.06%+50 B 🔺
@sentry/aws-serverless92.35 kB+0.06%+51 B 🔺
@sentry/cloudflare (withSentry) - minified218.65 kB+0.01%+9 B 🔺
@sentry/cloudflare (withSentry)538.85 kB+0.04%+196 B 🔺

View base workflow run

@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from a32a10f to bec33b0CompareJuly 27, 2026 08:41
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from bec33b0 to e303625CompareJuly 27, 2026 14:58
Comment threadpackages/core/src/client.ts
Base automatically changed from feat/span-streaming-default to developJuly 28, 2026 07:36
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch 2 times, most recently from a6f845e to 344055eCompareJuly 28, 2026 08:22
@Lms24Lms24 changed the title feat(core)!: Stream beforeSendSpan payloads by defaultfeat(core)!: Make beforeSendSpan compatible with streamed spans by defaultJul 28, 2026
@Lms24Lms24 self-assigned this Jul 28, 2026
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch 2 times, most recently from 9f59f8c to 9176c4bCompareJuly 29, 2026 15:37
@Lms24
Lms24 marked this pull request as ready for review July 29, 2026 15:42
@Lms24
Lms24 requested review from a team as code ownersJuly 29, 2026 15:42
@Lms24
Lms24 requested review from isaacs, msonnb, mydea, nicohrubec and s1gr1d and removed request for a teamJuly 29, 2026 15:42

@isaacsisaacs left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm not super familiar with this part of the machinery, so leaving it as a comment. If I'm misreading things or someone else disagrees, I'm happy to be overruled :)

Since span streaming is the direction we're moving, and the goal is to make it the default, moving these checks out of the integration and into the client feels like the right call.

Comment threadpackages/core/src/client.ts
Comment threadpackages/core/src/client.ts
Comment threadpackages/core/src/tracing/spans/spanJsonToStreamedSpan.ts Outdated
Comment threadpackages/core/src/tracing/spans/spanJsonToStreamedSpan.ts Outdated
Comment threadpackages/core/src/types/options.ts Outdated
Comment threadpackages/core/test/lib/tracing/spans/captureSpan.test.ts Outdated
Comment threadpackages/core/test/lib/tracing/sentrySpan.test.ts Outdated
Comment threadpackages/core/test/lib/tracing/spans/spanJsonToStreamedSpan.test.ts Outdated
Comment threadMIGRATION.md
Comment threadpackages/core/test/lib/tracing/sentrySpan.test.ts Outdated
Comment threadpackages/core/test/lib/client.test.ts
Lms24 added a commit that referenced this pull request Jul 31, 2026
…` and log warnings if used with streaming (#22908)
This PR:
1. Deprecates `beforeSendTransaction` and `ignoreTransactions`
2. Logs a console warning (intentionally not gated with `debug: true`)
to warn users if they use any of the two options with span streaming
enabled
I intentionally didn't update the migration guide. this is handled in
#22643.
more details in
#22856closes#22856closes#20279
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from 0ec024e to 4910062CompareAugust 3, 2026 12:48
Comment threadpackages/core/src/tracing/sentrySpan.ts Outdated
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from c3ecf63 to 0749d71CompareAugust 3, 2026 14:31
@Lms24
Lms24 marked this pull request as ready for review August 3, 2026 14:48

@chargomechargome left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM just two questions!

Comment threadpackages/browser/src/integrations/spanstreaming.ts
Comment threadpackages/core/src/tracing/spans/captureSpan.ts Outdated
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from 9ef7183 to f6cfc05CompareAugust 4, 2026 09:23

@cursorcursorBot 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 f6cfc05. Configure here.

Comment threadpackages/core/test/lib/tracing/spans/captureSpan.test.ts
Comment threadpackages/core/src/client.ts
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from 9dcfb44 to 6fb09a1CompareAugust 4, 2026 13:33
Lms24and others added 13 commits August 4, 2026 15:47
Make streamed span JSON the default beforeSendSpan contract and provide
withStaticSpan for callbacks that still process transaction span JSON.
Deprecate withStreamedSpan for removal in version 12.
BREAKING CHANGE: beforeSendSpan receives StreamedSpanJSON by default.
Fixes#22349
Co-Authored-By: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
`withStreamedSpan` no longer marks its callback. Nothing reads `_streamed` since
`isStreamedBeforeSendSpanCallback` became "not wrapped with `withStaticSpan`", so the
wrapper returns the callback unchanged instead of mutating a function it was handed.
Resolve `traceLifecycle` once in the `Client` constructor, where any value other than
`'static'` normalizes to the `'stream'` default. Neither span streaming integration
writes back to the option anymore, which removes an ordering hazard: integrations that
read `hasSpanStreamingEnabled` during their own `setup` could observe the value before
or after the mutation depending on integration order. The registration gates now test
`!== 'static'` so they agree with the normalized value; previously an unknown value
failed the gate but still resolved to `'stream'`, leaving nothing to flush spans.
Move the `beforeSendSpan` format check into the constructor as well. A callback is only
invoked for the span format matching the trace lifecycle, so a mismatch means it is
never called. The client can warn where the integration could not: with
`traceLifecycle: 'static'` the streaming integration is never registered, so an
unwrapped callback previously went unreported.
A `withStaticSpan` callback under `traceLifecycle: 'stream'` no longer downgrades the
lifecycle to `'static'`. Opting out of streaming requires setting
`traceLifecycle: 'static'` explicitly.
Refs #22349
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from 9810912 to 4d63933CompareAugust 4, 2026 13:47
Comment on lines 79 to +82
const processedSpan =
beforeSendSpan && isStreamedBeforeSendSpanCallback(beforeSendSpan)
// check for traceLifecycle here because in static lifecycle,
// captureSpan is called for INP spans. If an unmigrated beforeSendSpan
// callback is run on these spans, it will throw an error.

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.

Bug: In production builds, old beforeSendSpan callbacks expecting v1 SpanJSON format are silently invoked with the new v2 StreamedSpanJSON format, leading to incorrect span processing.
Severity: HIGH

Suggested Fix

Implement a runtime check within captureSpan to prevent v1-style beforeSendSpan callbacks from being invoked with v2 StreamedSpanJSON data in production. Either skip these incompatible callbacks or throw an error to make the breaking change explicit, rather than allowing silent data corruption. The current DEBUG_BUILD warnings are insufficient as they don't run in production.

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/tracing/spans/captureSpan.ts#L79-L82
Potential issue: With the new default `traceLifecycle: 'stream'`, the `captureSpan`
function now invokes `beforeSendSpan` callbacks that are not wrapped with
`withStaticSpan`. If a user has an older callback designed for the v1 `SpanJSON` format
(expecting fields like `description`), it will now be called with the v2
`StreamedSpanJSON` format (with fields like `name` and `end_timestamp`). This mismatch
will cause the callback to silently process incorrect data, leading to failed or
improper span modification. While development builds have warnings, these are stripped
from production builds, making this a silent failure for users who update.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

this is somewhat wrong because we already warn about this and users' callbacks will "break" if they type-check. Another thing we'll fix in a follow-up is to try/catch beforeSendSpan callback invocations, so that the SDK doesn't crash users.

@Lms24
Lms24 merged commit 5e76abe into developAug 4, 2026
266 checks passed
@Lms24
Lms24 deleted the feat/before-send-span-streamed-default branch August 4, 2026 15:34
Sign up for freeto 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.

Make streamed span types the default in public APIs

3 participants

@Lms24@isaacs@chargome
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(core)!: Make beforeSendSpan compatible with streamed spans by default - #22643

Merged
Lms24 merged 13 commits into
developfrom
feat/before-send-span-streamed-default
Aug 4, 2026
Merged

feat(core)!: Make beforeSendSpan compatible with streamed spans by default#22643
Lms24 merged 13 commits into
developfrom
feat/before-send-span-streamed-default

Conversation

@Lms24

@Lms24Lms24 commented Jul 24, 2026

Copy link
Copy Markdown
Member

This PR changes beforeSendSpan to receive StreamedSpanJSON by default, matching the new trace lifecycle default.

Callbacks that intentionally process legacy transaction span JSON need to add the withStaticSpan wrapper that marks the callback as "static"-compatible and hands users the SpanJSON type they used in the callback beforehand.

The withStreamedSpanwrapper remains available as a deprecated compatibility helper and is scheduled for removal in version 12.

More changes:

  • invalid beforeSendSpan callbacks no longer lead to switching the traceLifecycle. Since it now has a default and users need to actively opt out of span streaming, I think it's fair to treat incompatible callbacks as "invalid" and hence skip over them.
  • The beforeSendSpan compatibility checks were moved from the integrations into the core client which ensures that they always run now, even if users selected the static life cycle and hence spanStreamingIntegration doesn't get added.
  • Because we removed sending INP spans as v1 (SpanJSON) spans in favour of always sending them as v2 spans, we now convert a StreamedSpanJson to SpanJson in captureSpan, hand it to the static callback and then convert it back. Not great but I think we need to let users still scrub INP spans.

Closes#22349

@github-actions

github-actionsBot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser29.83 kB+0.13%+37 B 🔺
@sentry/browser - with treeshaking flags28.03 kB+0.05%+14 B 🔺
@sentry/browser (incl. Tracing)47.25 kB+0.09%+42 B 🔺
@sentry/browser (incl. Tracing + Span Streaming)47.26 kB+0.1%+44 B 🔺
@sentry/browser (incl. Tracing, Profiling)51.98 kB+0.09%+43 B 🔺
@sentry/browser (incl. Tracing, Replay)86.58 kB+0.08%+61 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags76 kB+0.06%+42 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas)91.27 kB+0.04%+28 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback)103.92 kB+0.05%+42 B 🔺
@sentry/browser (incl. Feedback)47.13 kB+0.08%+33 B 🔺
@sentry/browser (incl. sendFeedback)34.67 kB+0.08%+27 B 🔺
@sentry/browser (incl. FeedbackAsync)39.78 kB+0.08%+30 B 🔺
@sentry/browser (incl. Metrics)30.91 kB+0.14%+41 B 🔺
@sentry/browser (incl. Logs)31.14 kB+0.13%+39 B 🔺
@sentry/browser (incl. Metrics & Logs)31.81 kB+0.12%+38 B 🔺
@sentry/react31.63 kB+0.12%+36 B 🔺
@sentry/react (incl. Tracing)49.51 kB+0.14%+69 B 🔺
@sentry/vue34.92 kB+0.17%+57 B 🔺
@sentry/vue (incl. Tracing)49.23 kB+0.1%+46 B 🔺
@sentry/svelte29.86 kB+0.13%+36 B 🔺
CDN Bundle31.87 kB+0.01%+1 B 🔺
CDN Bundle (incl. Tracing)47.52 kB+0.01%+1 B 🔺
CDN Bundle (incl. Logs, Metrics)33.41 kB+0.02%+4 B 🔺
CDN Bundle (incl. Tracing, Logs, Metrics)48.93 kB-0.02%-6 B 🔽
CDN Bundle (incl. Replay, Logs, Metrics)72.75 kB-0.01%-2 B 🔽
CDN Bundle (incl. Tracing, Replay)85.18 kB-0.01%-8 B 🔽
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)86.49 kB-0.01%-5 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback)91 kB-0.01%-9 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)92.31 kB-0.02%-10 B 🔽
CDN Bundle - uncompressed94.62 kB-0.09%-84 B 🔽
CDN Bundle (incl. Tracing) - uncompressed142.1 kB-0.05%-57 B 🔽
CDN Bundle (incl. Logs, Metrics) - uncompressed99.24 kB-0.09%-84 B 🔽
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed146.08 kB-0.04%-57 B 🔽
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed223.94 kB-0.04%-84 B 🔽
CDN Bundle (incl. Tracing, Replay) - uncompressed261.36 kB-0.03%-57 B 🔽
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed265.32 kB-0.03%-57 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed275.06 kB-0.03%-57 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed279.01 kB-0.03%-57 B 🔽
@sentry/nextjs (client)52.07 kB+0.12%+61 B 🔺
@sentry/sveltekit (client)47.7 kB+0.14%+62 B 🔺
@sentry/core/server65.52 kB+0.04%+23 B 🔺
@sentry/core/browser51.88 kB+0.09%+43 B 🔺
@sentry/node120.24 kB+0.05%+49 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection)0 Baddedadded
@sentry/node - without tracing83.69 kB+0.06%+50 B 🔺
@sentry/aws-serverless92.35 kB+0.06%+51 B 🔺
@sentry/cloudflare (withSentry) - minified218.65 kB+0.01%+9 B 🔺
@sentry/cloudflare (withSentry)538.85 kB+0.04%+196 B 🔺

View base workflow run

@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from a32a10f to bec33b0CompareJuly 27, 2026 08:41
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from bec33b0 to e303625CompareJuly 27, 2026 14:58
Comment threadpackages/core/src/client.ts
Base automatically changed from feat/span-streaming-default to developJuly 28, 2026 07:36
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch 2 times, most recently from a6f845e to 344055eCompareJuly 28, 2026 08:22
@Lms24Lms24 changed the title feat(core)!: Stream beforeSendSpan payloads by defaultfeat(core)!: Make beforeSendSpan compatible with streamed spans by defaultJul 28, 2026
@Lms24Lms24 self-assigned this Jul 28, 2026
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch 2 times, most recently from 9f59f8c to 9176c4bCompareJuly 29, 2026 15:37
@Lms24
Lms24 marked this pull request as ready for review July 29, 2026 15:42
@Lms24
Lms24 requested review from a team as code ownersJuly 29, 2026 15:42
@Lms24
Lms24 requested review from isaacs, msonnb, mydea, nicohrubec and s1gr1d and removed request for a teamJuly 29, 2026 15:42

@isaacsisaacs left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm not super familiar with this part of the machinery, so leaving it as a comment. If I'm misreading things or someone else disagrees, I'm happy to be overruled :)

Since span streaming is the direction we're moving, and the goal is to make it the default, moving these checks out of the integration and into the client feels like the right call.

Comment threadpackages/core/src/client.ts
Comment threadpackages/core/src/client.ts
Comment threadpackages/core/src/tracing/spans/spanJsonToStreamedSpan.ts Outdated
Comment threadpackages/core/src/tracing/spans/spanJsonToStreamedSpan.ts Outdated
Comment threadpackages/core/src/types/options.ts Outdated
Comment threadpackages/core/test/lib/tracing/spans/captureSpan.test.ts Outdated
Comment threadpackages/core/test/lib/tracing/sentrySpan.test.ts Outdated
Comment threadpackages/core/test/lib/tracing/spans/spanJsonToStreamedSpan.test.ts Outdated
Comment threadMIGRATION.md
Comment threadpackages/core/test/lib/tracing/sentrySpan.test.ts Outdated
Comment threadpackages/core/test/lib/client.test.ts
Lms24 added a commit that referenced this pull request Jul 31, 2026
…` and log warnings if used with streaming (#22908)
This PR:
1. Deprecates `beforeSendTransaction` and `ignoreTransactions`
2. Logs a console warning (intentionally not gated with `debug: true`)
to warn users if they use any of the two options with span streaming
enabled
I intentionally didn't update the migration guide. this is handled in
#22643.
more details in
#22856closes#22856closes#20279
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from 0ec024e to 4910062CompareAugust 3, 2026 12:48
Comment threadpackages/core/src/tracing/sentrySpan.ts Outdated
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from c3ecf63 to 0749d71CompareAugust 3, 2026 14:31
@Lms24
Lms24 marked this pull request as ready for review August 3, 2026 14:48

@chargomechargome left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM just two questions!

Comment threadpackages/browser/src/integrations/spanstreaming.ts
Comment threadpackages/core/src/tracing/spans/captureSpan.ts Outdated
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from 9ef7183 to f6cfc05CompareAugust 4, 2026 09:23

@cursorcursorBot 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 f6cfc05. Configure here.

Comment threadpackages/core/test/lib/tracing/spans/captureSpan.test.ts
Comment threadpackages/core/src/client.ts
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from 9dcfb44 to 6fb09a1CompareAugust 4, 2026 13:33
Lms24and others added 13 commits August 4, 2026 15:47
Make streamed span JSON the default beforeSendSpan contract and provide
withStaticSpan for callbacks that still process transaction span JSON.
Deprecate withStreamedSpan for removal in version 12.
BREAKING CHANGE: beforeSendSpan receives StreamedSpanJSON by default.
Fixes#22349
Co-Authored-By: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
`withStreamedSpan` no longer marks its callback. Nothing reads `_streamed` since
`isStreamedBeforeSendSpanCallback` became "not wrapped with `withStaticSpan`", so the
wrapper returns the callback unchanged instead of mutating a function it was handed.
Resolve `traceLifecycle` once in the `Client` constructor, where any value other than
`'static'` normalizes to the `'stream'` default. Neither span streaming integration
writes back to the option anymore, which removes an ordering hazard: integrations that
read `hasSpanStreamingEnabled` during their own `setup` could observe the value before
or after the mutation depending on integration order. The registration gates now test
`!== 'static'` so they agree with the normalized value; previously an unknown value
failed the gate but still resolved to `'stream'`, leaving nothing to flush spans.
Move the `beforeSendSpan` format check into the constructor as well. A callback is only
invoked for the span format matching the trace lifecycle, so a mismatch means it is
never called. The client can warn where the integration could not: with
`traceLifecycle: 'static'` the streaming integration is never registered, so an
unwrapped callback previously went unreported.
A `withStaticSpan` callback under `traceLifecycle: 'stream'` no longer downgrades the
lifecycle to `'static'`. Opting out of streaming requires setting
`traceLifecycle: 'static'` explicitly.
Refs #22349
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from 9810912 to 4d63933CompareAugust 4, 2026 13:47
Comment on lines 79 to +82
const processedSpan =
beforeSendSpan && isStreamedBeforeSendSpanCallback(beforeSendSpan)
// check for traceLifecycle here because in static lifecycle,
// captureSpan is called for INP spans. If an unmigrated beforeSendSpan
// callback is run on these spans, it will throw an error.

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.

Bug: In production builds, old beforeSendSpan callbacks expecting v1 SpanJSON format are silently invoked with the new v2 StreamedSpanJSON format, leading to incorrect span processing.
Severity: HIGH

Suggested Fix

Implement a runtime check within captureSpan to prevent v1-style beforeSendSpan callbacks from being invoked with v2 StreamedSpanJSON data in production. Either skip these incompatible callbacks or throw an error to make the breaking change explicit, rather than allowing silent data corruption. The current DEBUG_BUILD warnings are insufficient as they don't run in production.

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/tracing/spans/captureSpan.ts#L79-L82
Potential issue: With the new default `traceLifecycle: 'stream'`, the `captureSpan`
function now invokes `beforeSendSpan` callbacks that are not wrapped with
`withStaticSpan`. If a user has an older callback designed for the v1 `SpanJSON` format
(expecting fields like `description`), it will now be called with the v2
`StreamedSpanJSON` format (with fields like `name` and `end_timestamp`). This mismatch
will cause the callback to silently process incorrect data, leading to failed or
improper span modification. While development builds have warnings, these are stripped
from production builds, making this a silent failure for users who update.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

this is somewhat wrong because we already warn about this and users' callbacks will "break" if they type-check. Another thing we'll fix in a follow-up is to try/catch beforeSendSpan callback invocations, so that the SDK doesn't crash users.

@Lms24
Lms24 merged commit 5e76abe into developAug 4, 2026
266 checks passed
@Lms24
Lms24 deleted the feat/before-send-span-streamed-default branch August 4, 2026 15:34
Sign up for freeto 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.

Make streamed span types the default in public APIs

3 participants

@Lms24@isaacs@chargome
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(core)!: Make beforeSendSpan compatible with streamed spans by default - #22643

Merged
Lms24 merged 13 commits into
developfrom
feat/before-send-span-streamed-default
Aug 4, 2026
Merged

feat(core)!: Make beforeSendSpan compatible with streamed spans by default#22643
Lms24 merged 13 commits into
developfrom
feat/before-send-span-streamed-default

Conversation

@Lms24

@Lms24Lms24 commented Jul 24, 2026

Copy link
Copy Markdown
Member

This PR changes beforeSendSpan to receive StreamedSpanJSON by default, matching the new trace lifecycle default.

Callbacks that intentionally process legacy transaction span JSON need to add the withStaticSpan wrapper that marks the callback as "static"-compatible and hands users the SpanJSON type they used in the callback beforehand.

The withStreamedSpanwrapper remains available as a deprecated compatibility helper and is scheduled for removal in version 12.

More changes:

  • invalid beforeSendSpan callbacks no longer lead to switching the traceLifecycle. Since it now has a default and users need to actively opt out of span streaming, I think it's fair to treat incompatible callbacks as "invalid" and hence skip over them.
  • The beforeSendSpan compatibility checks were moved from the integrations into the core client which ensures that they always run now, even if users selected the static life cycle and hence spanStreamingIntegration doesn't get added.
  • Because we removed sending INP spans as v1 (SpanJSON) spans in favour of always sending them as v2 spans, we now convert a StreamedSpanJson to SpanJson in captureSpan, hand it to the static callback and then convert it back. Not great but I think we need to let users still scrub INP spans.

Closes#22349

@github-actions

github-actionsBot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser29.83 kB+0.13%+37 B 🔺
@sentry/browser - with treeshaking flags28.03 kB+0.05%+14 B 🔺
@sentry/browser (incl. Tracing)47.25 kB+0.09%+42 B 🔺
@sentry/browser (incl. Tracing + Span Streaming)47.26 kB+0.1%+44 B 🔺
@sentry/browser (incl. Tracing, Profiling)51.98 kB+0.09%+43 B 🔺
@sentry/browser (incl. Tracing, Replay)86.58 kB+0.08%+61 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags76 kB+0.06%+42 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas)91.27 kB+0.04%+28 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback)103.92 kB+0.05%+42 B 🔺
@sentry/browser (incl. Feedback)47.13 kB+0.08%+33 B 🔺
@sentry/browser (incl. sendFeedback)34.67 kB+0.08%+27 B 🔺
@sentry/browser (incl. FeedbackAsync)39.78 kB+0.08%+30 B 🔺
@sentry/browser (incl. Metrics)30.91 kB+0.14%+41 B 🔺
@sentry/browser (incl. Logs)31.14 kB+0.13%+39 B 🔺
@sentry/browser (incl. Metrics & Logs)31.81 kB+0.12%+38 B 🔺
@sentry/react31.63 kB+0.12%+36 B 🔺
@sentry/react (incl. Tracing)49.51 kB+0.14%+69 B 🔺
@sentry/vue34.92 kB+0.17%+57 B 🔺
@sentry/vue (incl. Tracing)49.23 kB+0.1%+46 B 🔺
@sentry/svelte29.86 kB+0.13%+36 B 🔺
CDN Bundle31.87 kB+0.01%+1 B 🔺
CDN Bundle (incl. Tracing)47.52 kB+0.01%+1 B 🔺
CDN Bundle (incl. Logs, Metrics)33.41 kB+0.02%+4 B 🔺
CDN Bundle (incl. Tracing, Logs, Metrics)48.93 kB-0.02%-6 B 🔽
CDN Bundle (incl. Replay, Logs, Metrics)72.75 kB-0.01%-2 B 🔽
CDN Bundle (incl. Tracing, Replay)85.18 kB-0.01%-8 B 🔽
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)86.49 kB-0.01%-5 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback)91 kB-0.01%-9 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)92.31 kB-0.02%-10 B 🔽
CDN Bundle - uncompressed94.62 kB-0.09%-84 B 🔽
CDN Bundle (incl. Tracing) - uncompressed142.1 kB-0.05%-57 B 🔽
CDN Bundle (incl. Logs, Metrics) - uncompressed99.24 kB-0.09%-84 B 🔽
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed146.08 kB-0.04%-57 B 🔽
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed223.94 kB-0.04%-84 B 🔽
CDN Bundle (incl. Tracing, Replay) - uncompressed261.36 kB-0.03%-57 B 🔽
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed265.32 kB-0.03%-57 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed275.06 kB-0.03%-57 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed279.01 kB-0.03%-57 B 🔽
@sentry/nextjs (client)52.07 kB+0.12%+61 B 🔺
@sentry/sveltekit (client)47.7 kB+0.14%+62 B 🔺
@sentry/core/server65.52 kB+0.04%+23 B 🔺
@sentry/core/browser51.88 kB+0.09%+43 B 🔺
@sentry/node120.24 kB+0.05%+49 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection)0 Baddedadded
@sentry/node - without tracing83.69 kB+0.06%+50 B 🔺
@sentry/aws-serverless92.35 kB+0.06%+51 B 🔺
@sentry/cloudflare (withSentry) - minified218.65 kB+0.01%+9 B 🔺
@sentry/cloudflare (withSentry)538.85 kB+0.04%+196 B 🔺

View base workflow run

@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from a32a10f to bec33b0CompareJuly 27, 2026 08:41
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from bec33b0 to e303625CompareJuly 27, 2026 14:58
Comment threadpackages/core/src/client.ts
Base automatically changed from feat/span-streaming-default to developJuly 28, 2026 07:36
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch 2 times, most recently from a6f845e to 344055eCompareJuly 28, 2026 08:22
@Lms24Lms24 changed the title feat(core)!: Stream beforeSendSpan payloads by defaultfeat(core)!: Make beforeSendSpan compatible with streamed spans by defaultJul 28, 2026
@Lms24Lms24 self-assigned this Jul 28, 2026
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch 2 times, most recently from 9f59f8c to 9176c4bCompareJuly 29, 2026 15:37
@Lms24
Lms24 marked this pull request as ready for review July 29, 2026 15:42
@Lms24
Lms24 requested review from a team as code ownersJuly 29, 2026 15:42
@Lms24
Lms24 requested review from isaacs, msonnb, mydea, nicohrubec and s1gr1d and removed request for a teamJuly 29, 2026 15:42

@isaacsisaacs left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm not super familiar with this part of the machinery, so leaving it as a comment. If I'm misreading things or someone else disagrees, I'm happy to be overruled :)

Since span streaming is the direction we're moving, and the goal is to make it the default, moving these checks out of the integration and into the client feels like the right call.

Comment threadpackages/core/src/client.ts
Comment threadpackages/core/src/client.ts
Comment threadpackages/core/src/tracing/spans/spanJsonToStreamedSpan.ts Outdated
Comment threadpackages/core/src/tracing/spans/spanJsonToStreamedSpan.ts Outdated
Comment threadpackages/core/src/types/options.ts Outdated
Comment threadpackages/core/test/lib/tracing/spans/captureSpan.test.ts Outdated
Comment threadpackages/core/test/lib/tracing/sentrySpan.test.ts Outdated
Comment threadpackages/core/test/lib/tracing/spans/spanJsonToStreamedSpan.test.ts Outdated
Comment threadMIGRATION.md
Comment threadpackages/core/test/lib/tracing/sentrySpan.test.ts Outdated
Comment threadpackages/core/test/lib/client.test.ts
Lms24 added a commit that referenced this pull request Jul 31, 2026
…` and log warnings if used with streaming (#22908)
This PR:
1. Deprecates `beforeSendTransaction` and `ignoreTransactions`
2. Logs a console warning (intentionally not gated with `debug: true`)
to warn users if they use any of the two options with span streaming
enabled
I intentionally didn't update the migration guide. this is handled in
#22643.
more details in
#22856closes#22856closes#20279
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from 0ec024e to 4910062CompareAugust 3, 2026 12:48
Comment threadpackages/core/src/tracing/sentrySpan.ts Outdated
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from c3ecf63 to 0749d71CompareAugust 3, 2026 14:31
@Lms24
Lms24 marked this pull request as ready for review August 3, 2026 14:48

@chargomechargome left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM just two questions!

Comment threadpackages/browser/src/integrations/spanstreaming.ts
Comment threadpackages/core/src/tracing/spans/captureSpan.ts Outdated
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from 9ef7183 to f6cfc05CompareAugust 4, 2026 09:23

@cursorcursorBot 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 f6cfc05. Configure here.

Comment threadpackages/core/test/lib/tracing/spans/captureSpan.test.ts
Comment threadpackages/core/src/client.ts
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from 9dcfb44 to 6fb09a1CompareAugust 4, 2026 13:33
Lms24and others added 13 commits August 4, 2026 15:47
Make streamed span JSON the default beforeSendSpan contract and provide
withStaticSpan for callbacks that still process transaction span JSON.
Deprecate withStreamedSpan for removal in version 12.
BREAKING CHANGE: beforeSendSpan receives StreamedSpanJSON by default.
Fixes#22349
Co-Authored-By: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
`withStreamedSpan` no longer marks its callback. Nothing reads `_streamed` since
`isStreamedBeforeSendSpanCallback` became "not wrapped with `withStaticSpan`", so the
wrapper returns the callback unchanged instead of mutating a function it was handed.
Resolve `traceLifecycle` once in the `Client` constructor, where any value other than
`'static'` normalizes to the `'stream'` default. Neither span streaming integration
writes back to the option anymore, which removes an ordering hazard: integrations that
read `hasSpanStreamingEnabled` during their own `setup` could observe the value before
or after the mutation depending on integration order. The registration gates now test
`!== 'static'` so they agree with the normalized value; previously an unknown value
failed the gate but still resolved to `'stream'`, leaving nothing to flush spans.
Move the `beforeSendSpan` format check into the constructor as well. A callback is only
invoked for the span format matching the trace lifecycle, so a mismatch means it is
never called. The client can warn where the integration could not: with
`traceLifecycle: 'static'` the streaming integration is never registered, so an
unwrapped callback previously went unreported.
A `withStaticSpan` callback under `traceLifecycle: 'stream'` no longer downgrades the
lifecycle to `'static'`. Opting out of streaming requires setting
`traceLifecycle: 'static'` explicitly.
Refs #22349
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from 9810912 to 4d63933CompareAugust 4, 2026 13:47
Comment on lines 79 to +82
const processedSpan =
beforeSendSpan && isStreamedBeforeSendSpanCallback(beforeSendSpan)
// check for traceLifecycle here because in static lifecycle,
// captureSpan is called for INP spans. If an unmigrated beforeSendSpan
// callback is run on these spans, it will throw an error.

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.

Bug: In production builds, old beforeSendSpan callbacks expecting v1 SpanJSON format are silently invoked with the new v2 StreamedSpanJSON format, leading to incorrect span processing.
Severity: HIGH

Suggested Fix

Implement a runtime check within captureSpan to prevent v1-style beforeSendSpan callbacks from being invoked with v2 StreamedSpanJSON data in production. Either skip these incompatible callbacks or throw an error to make the breaking change explicit, rather than allowing silent data corruption. The current DEBUG_BUILD warnings are insufficient as they don't run in production.

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/tracing/spans/captureSpan.ts#L79-L82
Potential issue: With the new default `traceLifecycle: 'stream'`, the `captureSpan`
function now invokes `beforeSendSpan` callbacks that are not wrapped with
`withStaticSpan`. If a user has an older callback designed for the v1 `SpanJSON` format
(expecting fields like `description`), it will now be called with the v2
`StreamedSpanJSON` format (with fields like `name` and `end_timestamp`). This mismatch
will cause the callback to silently process incorrect data, leading to failed or
improper span modification. While development builds have warnings, these are stripped
from production builds, making this a silent failure for users who update.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

this is somewhat wrong because we already warn about this and users' callbacks will "break" if they type-check. Another thing we'll fix in a follow-up is to try/catch beforeSendSpan callback invocations, so that the SDK doesn't crash users.

@Lms24
Lms24 merged commit 5e76abe into developAug 4, 2026
266 checks passed
@Lms24
Lms24 deleted the feat/before-send-span-streamed-default branch August 4, 2026 15:34
Sign up for freeto 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.

Make streamed span types the default in public APIs

3 participants

@Lms24@isaacs@chargome
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

feat(core)!: Make beforeSendSpan compatible with streamed spans by default - #22643

Merged
Lms24 merged 13 commits into
developfrom
feat/before-send-span-streamed-default
Aug 4, 2026
Merged

feat(core)!: Make beforeSendSpan compatible with streamed spans by default#22643
Lms24 merged 13 commits into
developfrom
feat/before-send-span-streamed-default

Conversation

@Lms24

@Lms24Lms24 commented Jul 24, 2026

Copy link
Copy Markdown
Member

This PR changes beforeSendSpan to receive StreamedSpanJSON by default, matching the new trace lifecycle default.

Callbacks that intentionally process legacy transaction span JSON need to add the withStaticSpan wrapper that marks the callback as "static"-compatible and hands users the SpanJSON type they used in the callback beforehand.

The withStreamedSpanwrapper remains available as a deprecated compatibility helper and is scheduled for removal in version 12.

More changes:

  • invalid beforeSendSpan callbacks no longer lead to switching the traceLifecycle. Since it now has a default and users need to actively opt out of span streaming, I think it's fair to treat incompatible callbacks as "invalid" and hence skip over them.
  • The beforeSendSpan compatibility checks were moved from the integrations into the core client which ensures that they always run now, even if users selected the static life cycle and hence spanStreamingIntegration doesn't get added.
  • Because we removed sending INP spans as v1 (SpanJSON) spans in favour of always sending them as v2 spans, we now convert a StreamedSpanJson to SpanJson in captureSpan, hand it to the static callback and then convert it back. Not great but I think we need to let users still scrub INP spans.

Closes#22349

@github-actions

github-actionsBot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser29.83 kB+0.13%+37 B 🔺
@sentry/browser - with treeshaking flags28.03 kB+0.05%+14 B 🔺
@sentry/browser (incl. Tracing)47.25 kB+0.09%+42 B 🔺
@sentry/browser (incl. Tracing + Span Streaming)47.26 kB+0.1%+44 B 🔺
@sentry/browser (incl. Tracing, Profiling)51.98 kB+0.09%+43 B 🔺
@sentry/browser (incl. Tracing, Replay)86.58 kB+0.08%+61 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags76 kB+0.06%+42 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas)91.27 kB+0.04%+28 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback)103.92 kB+0.05%+42 B 🔺
@sentry/browser (incl. Feedback)47.13 kB+0.08%+33 B 🔺
@sentry/browser (incl. sendFeedback)34.67 kB+0.08%+27 B 🔺
@sentry/browser (incl. FeedbackAsync)39.78 kB+0.08%+30 B 🔺
@sentry/browser (incl. Metrics)30.91 kB+0.14%+41 B 🔺
@sentry/browser (incl. Logs)31.14 kB+0.13%+39 B 🔺
@sentry/browser (incl. Metrics & Logs)31.81 kB+0.12%+38 B 🔺
@sentry/react31.63 kB+0.12%+36 B 🔺
@sentry/react (incl. Tracing)49.51 kB+0.14%+69 B 🔺
@sentry/vue34.92 kB+0.17%+57 B 🔺
@sentry/vue (incl. Tracing)49.23 kB+0.1%+46 B 🔺
@sentry/svelte29.86 kB+0.13%+36 B 🔺
CDN Bundle31.87 kB+0.01%+1 B 🔺
CDN Bundle (incl. Tracing)47.52 kB+0.01%+1 B 🔺
CDN Bundle (incl. Logs, Metrics)33.41 kB+0.02%+4 B 🔺
CDN Bundle (incl. Tracing, Logs, Metrics)48.93 kB-0.02%-6 B 🔽
CDN Bundle (incl. Replay, Logs, Metrics)72.75 kB-0.01%-2 B 🔽
CDN Bundle (incl. Tracing, Replay)85.18 kB-0.01%-8 B 🔽
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)86.49 kB-0.01%-5 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback)91 kB-0.01%-9 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)92.31 kB-0.02%-10 B 🔽
CDN Bundle - uncompressed94.62 kB-0.09%-84 B 🔽
CDN Bundle (incl. Tracing) - uncompressed142.1 kB-0.05%-57 B 🔽
CDN Bundle (incl. Logs, Metrics) - uncompressed99.24 kB-0.09%-84 B 🔽
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed146.08 kB-0.04%-57 B 🔽
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed223.94 kB-0.04%-84 B 🔽
CDN Bundle (incl. Tracing, Replay) - uncompressed261.36 kB-0.03%-57 B 🔽
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed265.32 kB-0.03%-57 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed275.06 kB-0.03%-57 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed279.01 kB-0.03%-57 B 🔽
@sentry/nextjs (client)52.07 kB+0.12%+61 B 🔺
@sentry/sveltekit (client)47.7 kB+0.14%+62 B 🔺
@sentry/core/server65.52 kB+0.04%+23 B 🔺
@sentry/core/browser51.88 kB+0.09%+43 B 🔺
@sentry/node120.24 kB+0.05%+49 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection)0 Baddedadded
@sentry/node - without tracing83.69 kB+0.06%+50 B 🔺
@sentry/aws-serverless92.35 kB+0.06%+51 B 🔺
@sentry/cloudflare (withSentry) - minified218.65 kB+0.01%+9 B 🔺
@sentry/cloudflare (withSentry)538.85 kB+0.04%+196 B 🔺

View base workflow run

@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from a32a10f to bec33b0CompareJuly 27, 2026 08:41
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from bec33b0 to e303625CompareJuly 27, 2026 14:58
Comment threadpackages/core/src/client.ts
Base automatically changed from feat/span-streaming-default to developJuly 28, 2026 07:36
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch 2 times, most recently from a6f845e to 344055eCompareJuly 28, 2026 08:22
@Lms24Lms24 changed the title feat(core)!: Stream beforeSendSpan payloads by defaultfeat(core)!: Make beforeSendSpan compatible with streamed spans by defaultJul 28, 2026
@Lms24Lms24 self-assigned this Jul 28, 2026
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch 2 times, most recently from 9f59f8c to 9176c4bCompareJuly 29, 2026 15:37
@Lms24
Lms24 marked this pull request as ready for review July 29, 2026 15:42
@Lms24
Lms24 requested review from a team as code ownersJuly 29, 2026 15:42
@Lms24
Lms24 requested review from isaacs, msonnb, mydea, nicohrubec and s1gr1d and removed request for a teamJuly 29, 2026 15:42

@isaacsisaacs left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm not super familiar with this part of the machinery, so leaving it as a comment. If I'm misreading things or someone else disagrees, I'm happy to be overruled :)

Since span streaming is the direction we're moving, and the goal is to make it the default, moving these checks out of the integration and into the client feels like the right call.

Comment threadpackages/core/src/client.ts
Comment threadpackages/core/src/client.ts
Comment threadpackages/core/src/tracing/spans/spanJsonToStreamedSpan.ts Outdated
Comment threadpackages/core/src/tracing/spans/spanJsonToStreamedSpan.ts Outdated
Comment threadpackages/core/src/types/options.ts Outdated
Comment threadpackages/core/test/lib/tracing/spans/captureSpan.test.ts Outdated
Comment threadpackages/core/test/lib/tracing/sentrySpan.test.ts Outdated
Comment threadpackages/core/test/lib/tracing/spans/spanJsonToStreamedSpan.test.ts Outdated
Comment threadMIGRATION.md
Comment threadpackages/core/test/lib/tracing/sentrySpan.test.ts Outdated
Comment threadpackages/core/test/lib/client.test.ts
Lms24 added a commit that referenced this pull request Jul 31, 2026
…` and log warnings if used with streaming (#22908)
This PR:
1. Deprecates `beforeSendTransaction` and `ignoreTransactions`
2. Logs a console warning (intentionally not gated with `debug: true`)
to warn users if they use any of the two options with span streaming
enabled
I intentionally didn't update the migration guide. this is handled in
#22643.
more details in
#22856closes#22856closes#20279
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from 0ec024e to 4910062CompareAugust 3, 2026 12:48
Comment threadpackages/core/src/tracing/sentrySpan.ts Outdated
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from c3ecf63 to 0749d71CompareAugust 3, 2026 14:31
@Lms24
Lms24 marked this pull request as ready for review August 3, 2026 14:48

@chargomechargome left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM just two questions!

Comment threadpackages/browser/src/integrations/spanstreaming.ts
Comment threadpackages/core/src/tracing/spans/captureSpan.ts Outdated
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from 9ef7183 to f6cfc05CompareAugust 4, 2026 09:23

@cursorcursorBot 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 f6cfc05. Configure here.

Comment threadpackages/core/test/lib/tracing/spans/captureSpan.test.ts
Comment threadpackages/core/src/client.ts
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from 9dcfb44 to 6fb09a1CompareAugust 4, 2026 13:33
Lms24and others added 13 commits August 4, 2026 15:47
Make streamed span JSON the default beforeSendSpan contract and provide
withStaticSpan for callbacks that still process transaction span JSON.
Deprecate withStreamedSpan for removal in version 12.
BREAKING CHANGE: beforeSendSpan receives StreamedSpanJSON by default.
Fixes#22349
Co-Authored-By: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
`withStreamedSpan` no longer marks its callback. Nothing reads `_streamed` since
`isStreamedBeforeSendSpanCallback` became "not wrapped with `withStaticSpan`", so the
wrapper returns the callback unchanged instead of mutating a function it was handed.
Resolve `traceLifecycle` once in the `Client` constructor, where any value other than
`'static'` normalizes to the `'stream'` default. Neither span streaming integration
writes back to the option anymore, which removes an ordering hazard: integrations that
read `hasSpanStreamingEnabled` during their own `setup` could observe the value before
or after the mutation depending on integration order. The registration gates now test
`!== 'static'` so they agree with the normalized value; previously an unknown value
failed the gate but still resolved to `'stream'`, leaving nothing to flush spans.
Move the `beforeSendSpan` format check into the constructor as well. A callback is only
invoked for the span format matching the trace lifecycle, so a mismatch means it is
never called. The client can warn where the integration could not: with
`traceLifecycle: 'static'` the streaming integration is never registered, so an
unwrapped callback previously went unreported.
A `withStaticSpan` callback under `traceLifecycle: 'stream'` no longer downgrades the
lifecycle to `'static'`. Opting out of streaming requires setting
`traceLifecycle: 'static'` explicitly.
Refs #22349
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from 9810912 to 4d63933CompareAugust 4, 2026 13:47
Comment on lines 79 to +82
const processedSpan =
beforeSendSpan && isStreamedBeforeSendSpanCallback(beforeSendSpan)
// check for traceLifecycle here because in static lifecycle,
// captureSpan is called for INP spans. If an unmigrated beforeSendSpan
// callback is run on these spans, it will throw an error.

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.

Bug: In production builds, old beforeSendSpan callbacks expecting v1 SpanJSON format are silently invoked with the new v2 StreamedSpanJSON format, leading to incorrect span processing.
Severity: HIGH

Suggested Fix

Implement a runtime check within captureSpan to prevent v1-style beforeSendSpan callbacks from being invoked with v2 StreamedSpanJSON data in production. Either skip these incompatible callbacks or throw an error to make the breaking change explicit, rather than allowing silent data corruption. The current DEBUG_BUILD warnings are insufficient as they don't run in production.

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/tracing/spans/captureSpan.ts#L79-L82
Potential issue: With the new default `traceLifecycle: 'stream'`, the `captureSpan`
function now invokes `beforeSendSpan` callbacks that are not wrapped with
`withStaticSpan`. If a user has an older callback designed for the v1 `SpanJSON` format
(expecting fields like `description`), it will now be called with the v2
`StreamedSpanJSON` format (with fields like `name` and `end_timestamp`). This mismatch
will cause the callback to silently process incorrect data, leading to failed or
improper span modification. While development builds have warnings, these are stripped
from production builds, making this a silent failure for users who update.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

this is somewhat wrong because we already warn about this and users' callbacks will "break" if they type-check. Another thing we'll fix in a follow-up is to try/catch beforeSendSpan callback invocations, so that the SDK doesn't crash users.

@Lms24
Lms24 merged commit 5e76abe into developAug 4, 2026
266 checks passed
@Lms24
Lms24 deleted the feat/before-send-span-streamed-default branch August 4, 2026 15:34
Sign up for freeto 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.

Make streamed span types the default in public APIs

3 participants

@Lms24@isaacs@chargome
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(core)!: Make beforeSendSpan compatible with streamed spans by default - #22643

Merged
Lms24 merged 13 commits into
developfrom
feat/before-send-span-streamed-default
Aug 4, 2026
Merged

feat(core)!: Make beforeSendSpan compatible with streamed spans by default#22643
Lms24 merged 13 commits into
developfrom
feat/before-send-span-streamed-default

Conversation

@Lms24

@Lms24Lms24 commented Jul 24, 2026

Copy link
Copy Markdown
Member

This PR changes beforeSendSpan to receive StreamedSpanJSON by default, matching the new trace lifecycle default.

Callbacks that intentionally process legacy transaction span JSON need to add the withStaticSpan wrapper that marks the callback as "static"-compatible and hands users the SpanJSON type they used in the callback beforehand.

The withStreamedSpanwrapper remains available as a deprecated compatibility helper and is scheduled for removal in version 12.

More changes:

  • invalid beforeSendSpan callbacks no longer lead to switching the traceLifecycle. Since it now has a default and users need to actively opt out of span streaming, I think it's fair to treat incompatible callbacks as "invalid" and hence skip over them.
  • The beforeSendSpan compatibility checks were moved from the integrations into the core client which ensures that they always run now, even if users selected the static life cycle and hence spanStreamingIntegration doesn't get added.
  • Because we removed sending INP spans as v1 (SpanJSON) spans in favour of always sending them as v2 spans, we now convert a StreamedSpanJson to SpanJson in captureSpan, hand it to the static callback and then convert it back. Not great but I think we need to let users still scrub INP spans.

Closes#22349

@github-actions

github-actionsBot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser29.83 kB+0.13%+37 B 🔺
@sentry/browser - with treeshaking flags28.03 kB+0.05%+14 B 🔺
@sentry/browser (incl. Tracing)47.25 kB+0.09%+42 B 🔺
@sentry/browser (incl. Tracing + Span Streaming)47.26 kB+0.1%+44 B 🔺
@sentry/browser (incl. Tracing, Profiling)51.98 kB+0.09%+43 B 🔺
@sentry/browser (incl. Tracing, Replay)86.58 kB+0.08%+61 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags76 kB+0.06%+42 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas)91.27 kB+0.04%+28 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback)103.92 kB+0.05%+42 B 🔺
@sentry/browser (incl. Feedback)47.13 kB+0.08%+33 B 🔺
@sentry/browser (incl. sendFeedback)34.67 kB+0.08%+27 B 🔺
@sentry/browser (incl. FeedbackAsync)39.78 kB+0.08%+30 B 🔺
@sentry/browser (incl. Metrics)30.91 kB+0.14%+41 B 🔺
@sentry/browser (incl. Logs)31.14 kB+0.13%+39 B 🔺
@sentry/browser (incl. Metrics & Logs)31.81 kB+0.12%+38 B 🔺
@sentry/react31.63 kB+0.12%+36 B 🔺
@sentry/react (incl. Tracing)49.51 kB+0.14%+69 B 🔺
@sentry/vue34.92 kB+0.17%+57 B 🔺
@sentry/vue (incl. Tracing)49.23 kB+0.1%+46 B 🔺
@sentry/svelte29.86 kB+0.13%+36 B 🔺
CDN Bundle31.87 kB+0.01%+1 B 🔺
CDN Bundle (incl. Tracing)47.52 kB+0.01%+1 B 🔺
CDN Bundle (incl. Logs, Metrics)33.41 kB+0.02%+4 B 🔺
CDN Bundle (incl. Tracing, Logs, Metrics)48.93 kB-0.02%-6 B 🔽
CDN Bundle (incl. Replay, Logs, Metrics)72.75 kB-0.01%-2 B 🔽
CDN Bundle (incl. Tracing, Replay)85.18 kB-0.01%-8 B 🔽
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)86.49 kB-0.01%-5 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback)91 kB-0.01%-9 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)92.31 kB-0.02%-10 B 🔽
CDN Bundle - uncompressed94.62 kB-0.09%-84 B 🔽
CDN Bundle (incl. Tracing) - uncompressed142.1 kB-0.05%-57 B 🔽
CDN Bundle (incl. Logs, Metrics) - uncompressed99.24 kB-0.09%-84 B 🔽
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed146.08 kB-0.04%-57 B 🔽
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed223.94 kB-0.04%-84 B 🔽
CDN Bundle (incl. Tracing, Replay) - uncompressed261.36 kB-0.03%-57 B 🔽
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed265.32 kB-0.03%-57 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed275.06 kB-0.03%-57 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed279.01 kB-0.03%-57 B 🔽
@sentry/nextjs (client)52.07 kB+0.12%+61 B 🔺
@sentry/sveltekit (client)47.7 kB+0.14%+62 B 🔺
@sentry/core/server65.52 kB+0.04%+23 B 🔺
@sentry/core/browser51.88 kB+0.09%+43 B 🔺
@sentry/node120.24 kB+0.05%+49 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection)0 Baddedadded
@sentry/node - without tracing83.69 kB+0.06%+50 B 🔺
@sentry/aws-serverless92.35 kB+0.06%+51 B 🔺
@sentry/cloudflare (withSentry) - minified218.65 kB+0.01%+9 B 🔺
@sentry/cloudflare (withSentry)538.85 kB+0.04%+196 B 🔺

View base workflow run

@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from a32a10f to bec33b0CompareJuly 27, 2026 08:41
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from bec33b0 to e303625CompareJuly 27, 2026 14:58
Comment threadpackages/core/src/client.ts
Base automatically changed from feat/span-streaming-default to developJuly 28, 2026 07:36
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch 2 times, most recently from a6f845e to 344055eCompareJuly 28, 2026 08:22
@Lms24Lms24 changed the title feat(core)!: Stream beforeSendSpan payloads by defaultfeat(core)!: Make beforeSendSpan compatible with streamed spans by defaultJul 28, 2026
@Lms24Lms24 self-assigned this Jul 28, 2026
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch 2 times, most recently from 9f59f8c to 9176c4bCompareJuly 29, 2026 15:37
@Lms24
Lms24 marked this pull request as ready for review July 29, 2026 15:42
@Lms24
Lms24 requested review from a team as code ownersJuly 29, 2026 15:42
@Lms24
Lms24 requested review from isaacs, msonnb, mydea, nicohrubec and s1gr1d and removed request for a teamJuly 29, 2026 15:42

@isaacsisaacs left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm not super familiar with this part of the machinery, so leaving it as a comment. If I'm misreading things or someone else disagrees, I'm happy to be overruled :)

Since span streaming is the direction we're moving, and the goal is to make it the default, moving these checks out of the integration and into the client feels like the right call.

Comment threadpackages/core/src/client.ts
Comment threadpackages/core/src/client.ts
Comment threadpackages/core/src/tracing/spans/spanJsonToStreamedSpan.ts Outdated
Comment threadpackages/core/src/tracing/spans/spanJsonToStreamedSpan.ts Outdated
Comment threadpackages/core/src/types/options.ts Outdated
Comment threadpackages/core/test/lib/tracing/spans/captureSpan.test.ts Outdated
Comment threadpackages/core/test/lib/tracing/sentrySpan.test.ts Outdated
Comment threadpackages/core/test/lib/tracing/spans/spanJsonToStreamedSpan.test.ts Outdated
Comment threadMIGRATION.md
Comment threadpackages/core/test/lib/tracing/sentrySpan.test.ts Outdated
Comment threadpackages/core/test/lib/client.test.ts
Lms24 added a commit that referenced this pull request Jul 31, 2026
…` and log warnings if used with streaming (#22908)
This PR:
1. Deprecates `beforeSendTransaction` and `ignoreTransactions`
2. Logs a console warning (intentionally not gated with `debug: true`)
to warn users if they use any of the two options with span streaming
enabled
I intentionally didn't update the migration guide. this is handled in
#22643.
more details in
#22856closes#22856closes#20279
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from 0ec024e to 4910062CompareAugust 3, 2026 12:48
Comment threadpackages/core/src/tracing/sentrySpan.ts Outdated
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from c3ecf63 to 0749d71CompareAugust 3, 2026 14:31
@Lms24
Lms24 marked this pull request as ready for review August 3, 2026 14:48

@chargomechargome left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM just two questions!

Comment threadpackages/browser/src/integrations/spanstreaming.ts
Comment threadpackages/core/src/tracing/spans/captureSpan.ts Outdated
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from 9ef7183 to f6cfc05CompareAugust 4, 2026 09:23

@cursorcursorBot 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 f6cfc05. Configure here.

Comment threadpackages/core/test/lib/tracing/spans/captureSpan.test.ts
Comment threadpackages/core/src/client.ts
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from 9dcfb44 to 6fb09a1CompareAugust 4, 2026 13:33
Lms24and others added 13 commits August 4, 2026 15:47
Make streamed span JSON the default beforeSendSpan contract and provide
withStaticSpan for callbacks that still process transaction span JSON.
Deprecate withStreamedSpan for removal in version 12.
BREAKING CHANGE: beforeSendSpan receives StreamedSpanJSON by default.
Fixes#22349
Co-Authored-By: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
`withStreamedSpan` no longer marks its callback. Nothing reads `_streamed` since
`isStreamedBeforeSendSpanCallback` became "not wrapped with `withStaticSpan`", so the
wrapper returns the callback unchanged instead of mutating a function it was handed.
Resolve `traceLifecycle` once in the `Client` constructor, where any value other than
`'static'` normalizes to the `'stream'` default. Neither span streaming integration
writes back to the option anymore, which removes an ordering hazard: integrations that
read `hasSpanStreamingEnabled` during their own `setup` could observe the value before
or after the mutation depending on integration order. The registration gates now test
`!== 'static'` so they agree with the normalized value; previously an unknown value
failed the gate but still resolved to `'stream'`, leaving nothing to flush spans.
Move the `beforeSendSpan` format check into the constructor as well. A callback is only
invoked for the span format matching the trace lifecycle, so a mismatch means it is
never called. The client can warn where the integration could not: with
`traceLifecycle: 'static'` the streaming integration is never registered, so an
unwrapped callback previously went unreported.
A `withStaticSpan` callback under `traceLifecycle: 'stream'` no longer downgrades the
lifecycle to `'static'`. Opting out of streaming requires setting
`traceLifecycle: 'static'` explicitly.
Refs #22349
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from 9810912 to 4d63933CompareAugust 4, 2026 13:47
Comment on lines 79 to +82
const processedSpan =
beforeSendSpan && isStreamedBeforeSendSpanCallback(beforeSendSpan)
// check for traceLifecycle here because in static lifecycle,
// captureSpan is called for INP spans. If an unmigrated beforeSendSpan
// callback is run on these spans, it will throw an error.

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.

Bug: In production builds, old beforeSendSpan callbacks expecting v1 SpanJSON format are silently invoked with the new v2 StreamedSpanJSON format, leading to incorrect span processing.
Severity: HIGH

Suggested Fix

Implement a runtime check within captureSpan to prevent v1-style beforeSendSpan callbacks from being invoked with v2 StreamedSpanJSON data in production. Either skip these incompatible callbacks or throw an error to make the breaking change explicit, rather than allowing silent data corruption. The current DEBUG_BUILD warnings are insufficient as they don't run in production.

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/tracing/spans/captureSpan.ts#L79-L82
Potential issue: With the new default `traceLifecycle: 'stream'`, the `captureSpan`
function now invokes `beforeSendSpan` callbacks that are not wrapped with
`withStaticSpan`. If a user has an older callback designed for the v1 `SpanJSON` format
(expecting fields like `description`), it will now be called with the v2
`StreamedSpanJSON` format (with fields like `name` and `end_timestamp`). This mismatch
will cause the callback to silently process incorrect data, leading to failed or
improper span modification. While development builds have warnings, these are stripped
from production builds, making this a silent failure for users who update.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

this is somewhat wrong because we already warn about this and users' callbacks will "break" if they type-check. Another thing we'll fix in a follow-up is to try/catch beforeSendSpan callback invocations, so that the SDK doesn't crash users.

@Lms24
Lms24 merged commit 5e76abe into developAug 4, 2026
266 checks passed
@Lms24
Lms24 deleted the feat/before-send-span-streamed-default branch August 4, 2026 15:34
Sign up for freeto 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.

Make streamed span types the default in public APIs

3 participants

@Lms24@isaacs@chargome
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(core)!: Make beforeSendSpan compatible with streamed spans by default - #22643

Merged
Lms24 merged 13 commits into
developfrom
feat/before-send-span-streamed-default
Aug 4, 2026
Merged

feat(core)!: Make beforeSendSpan compatible with streamed spans by default#22643
Lms24 merged 13 commits into
developfrom
feat/before-send-span-streamed-default

Conversation

@Lms24

@Lms24Lms24 commented Jul 24, 2026

Copy link
Copy Markdown
Member

This PR changes beforeSendSpan to receive StreamedSpanJSON by default, matching the new trace lifecycle default.

Callbacks that intentionally process legacy transaction span JSON need to add the withStaticSpan wrapper that marks the callback as "static"-compatible and hands users the SpanJSON type they used in the callback beforehand.

The withStreamedSpanwrapper remains available as a deprecated compatibility helper and is scheduled for removal in version 12.

More changes:

  • invalid beforeSendSpan callbacks no longer lead to switching the traceLifecycle. Since it now has a default and users need to actively opt out of span streaming, I think it's fair to treat incompatible callbacks as "invalid" and hence skip over them.
  • The beforeSendSpan compatibility checks were moved from the integrations into the core client which ensures that they always run now, even if users selected the static life cycle and hence spanStreamingIntegration doesn't get added.
  • Because we removed sending INP spans as v1 (SpanJSON) spans in favour of always sending them as v2 spans, we now convert a StreamedSpanJson to SpanJson in captureSpan, hand it to the static callback and then convert it back. Not great but I think we need to let users still scrub INP spans.

Closes#22349

@github-actions

github-actionsBot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser29.83 kB+0.13%+37 B 🔺
@sentry/browser - with treeshaking flags28.03 kB+0.05%+14 B 🔺
@sentry/browser (incl. Tracing)47.25 kB+0.09%+42 B 🔺
@sentry/browser (incl. Tracing + Span Streaming)47.26 kB+0.1%+44 B 🔺
@sentry/browser (incl. Tracing, Profiling)51.98 kB+0.09%+43 B 🔺
@sentry/browser (incl. Tracing, Replay)86.58 kB+0.08%+61 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags76 kB+0.06%+42 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas)91.27 kB+0.04%+28 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback)103.92 kB+0.05%+42 B 🔺
@sentry/browser (incl. Feedback)47.13 kB+0.08%+33 B 🔺
@sentry/browser (incl. sendFeedback)34.67 kB+0.08%+27 B 🔺
@sentry/browser (incl. FeedbackAsync)39.78 kB+0.08%+30 B 🔺
@sentry/browser (incl. Metrics)30.91 kB+0.14%+41 B 🔺
@sentry/browser (incl. Logs)31.14 kB+0.13%+39 B 🔺
@sentry/browser (incl. Metrics & Logs)31.81 kB+0.12%+38 B 🔺
@sentry/react31.63 kB+0.12%+36 B 🔺
@sentry/react (incl. Tracing)49.51 kB+0.14%+69 B 🔺
@sentry/vue34.92 kB+0.17%+57 B 🔺
@sentry/vue (incl. Tracing)49.23 kB+0.1%+46 B 🔺
@sentry/svelte29.86 kB+0.13%+36 B 🔺
CDN Bundle31.87 kB+0.01%+1 B 🔺
CDN Bundle (incl. Tracing)47.52 kB+0.01%+1 B 🔺
CDN Bundle (incl. Logs, Metrics)33.41 kB+0.02%+4 B 🔺
CDN Bundle (incl. Tracing, Logs, Metrics)48.93 kB-0.02%-6 B 🔽
CDN Bundle (incl. Replay, Logs, Metrics)72.75 kB-0.01%-2 B 🔽
CDN Bundle (incl. Tracing, Replay)85.18 kB-0.01%-8 B 🔽
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)86.49 kB-0.01%-5 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback)91 kB-0.01%-9 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)92.31 kB-0.02%-10 B 🔽
CDN Bundle - uncompressed94.62 kB-0.09%-84 B 🔽
CDN Bundle (incl. Tracing) - uncompressed142.1 kB-0.05%-57 B 🔽
CDN Bundle (incl. Logs, Metrics) - uncompressed99.24 kB-0.09%-84 B 🔽
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed146.08 kB-0.04%-57 B 🔽
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed223.94 kB-0.04%-84 B 🔽
CDN Bundle (incl. Tracing, Replay) - uncompressed261.36 kB-0.03%-57 B 🔽
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed265.32 kB-0.03%-57 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed275.06 kB-0.03%-57 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed279.01 kB-0.03%-57 B 🔽
@sentry/nextjs (client)52.07 kB+0.12%+61 B 🔺
@sentry/sveltekit (client)47.7 kB+0.14%+62 B 🔺
@sentry/core/server65.52 kB+0.04%+23 B 🔺
@sentry/core/browser51.88 kB+0.09%+43 B 🔺
@sentry/node120.24 kB+0.05%+49 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection)0 Baddedadded
@sentry/node - without tracing83.69 kB+0.06%+50 B 🔺
@sentry/aws-serverless92.35 kB+0.06%+51 B 🔺
@sentry/cloudflare (withSentry) - minified218.65 kB+0.01%+9 B 🔺
@sentry/cloudflare (withSentry)538.85 kB+0.04%+196 B 🔺

View base workflow run

@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from a32a10f to bec33b0CompareJuly 27, 2026 08:41
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from bec33b0 to e303625CompareJuly 27, 2026 14:58
Comment threadpackages/core/src/client.ts
Base automatically changed from feat/span-streaming-default to developJuly 28, 2026 07:36
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch 2 times, most recently from a6f845e to 344055eCompareJuly 28, 2026 08:22
@Lms24Lms24 changed the title feat(core)!: Stream beforeSendSpan payloads by defaultfeat(core)!: Make beforeSendSpan compatible with streamed spans by defaultJul 28, 2026
@Lms24Lms24 self-assigned this Jul 28, 2026
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch 2 times, most recently from 9f59f8c to 9176c4bCompareJuly 29, 2026 15:37
@Lms24
Lms24 marked this pull request as ready for review July 29, 2026 15:42
@Lms24
Lms24 requested review from a team as code ownersJuly 29, 2026 15:42
@Lms24
Lms24 requested review from isaacs, msonnb, mydea, nicohrubec and s1gr1d and removed request for a teamJuly 29, 2026 15:42

@isaacsisaacs left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm not super familiar with this part of the machinery, so leaving it as a comment. If I'm misreading things or someone else disagrees, I'm happy to be overruled :)

Since span streaming is the direction we're moving, and the goal is to make it the default, moving these checks out of the integration and into the client feels like the right call.

Comment threadpackages/core/src/client.ts
Comment threadpackages/core/src/client.ts
Comment threadpackages/core/src/tracing/spans/spanJsonToStreamedSpan.ts Outdated
Comment threadpackages/core/src/tracing/spans/spanJsonToStreamedSpan.ts Outdated
Comment threadpackages/core/src/types/options.ts Outdated
Comment threadpackages/core/test/lib/tracing/spans/captureSpan.test.ts Outdated
Comment threadpackages/core/test/lib/tracing/sentrySpan.test.ts Outdated
Comment threadpackages/core/test/lib/tracing/spans/spanJsonToStreamedSpan.test.ts Outdated
Comment threadMIGRATION.md
Comment threadpackages/core/test/lib/tracing/sentrySpan.test.ts Outdated
Comment threadpackages/core/test/lib/client.test.ts
Lms24 added a commit that referenced this pull request Jul 31, 2026
…` and log warnings if used with streaming (#22908)
This PR:
1. Deprecates `beforeSendTransaction` and `ignoreTransactions`
2. Logs a console warning (intentionally not gated with `debug: true`)
to warn users if they use any of the two options with span streaming
enabled
I intentionally didn't update the migration guide. this is handled in
#22643.
more details in
#22856closes#22856closes#20279
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from 0ec024e to 4910062CompareAugust 3, 2026 12:48
Comment threadpackages/core/src/tracing/sentrySpan.ts Outdated
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from c3ecf63 to 0749d71CompareAugust 3, 2026 14:31
@Lms24
Lms24 marked this pull request as ready for review August 3, 2026 14:48

@chargomechargome left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM just two questions!

Comment threadpackages/browser/src/integrations/spanstreaming.ts
Comment threadpackages/core/src/tracing/spans/captureSpan.ts Outdated
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from 9ef7183 to f6cfc05CompareAugust 4, 2026 09:23

@cursorcursorBot 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 f6cfc05. Configure here.

Comment threadpackages/core/test/lib/tracing/spans/captureSpan.test.ts
Comment threadpackages/core/src/client.ts
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from 9dcfb44 to 6fb09a1CompareAugust 4, 2026 13:33
Lms24and others added 13 commits August 4, 2026 15:47
Make streamed span JSON the default beforeSendSpan contract and provide
withStaticSpan for callbacks that still process transaction span JSON.
Deprecate withStreamedSpan for removal in version 12.
BREAKING CHANGE: beforeSendSpan receives StreamedSpanJSON by default.
Fixes#22349
Co-Authored-By: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
`withStreamedSpan` no longer marks its callback. Nothing reads `_streamed` since
`isStreamedBeforeSendSpanCallback` became "not wrapped with `withStaticSpan`", so the
wrapper returns the callback unchanged instead of mutating a function it was handed.
Resolve `traceLifecycle` once in the `Client` constructor, where any value other than
`'static'` normalizes to the `'stream'` default. Neither span streaming integration
writes back to the option anymore, which removes an ordering hazard: integrations that
read `hasSpanStreamingEnabled` during their own `setup` could observe the value before
or after the mutation depending on integration order. The registration gates now test
`!== 'static'` so they agree with the normalized value; previously an unknown value
failed the gate but still resolved to `'stream'`, leaving nothing to flush spans.
Move the `beforeSendSpan` format check into the constructor as well. A callback is only
invoked for the span format matching the trace lifecycle, so a mismatch means it is
never called. The client can warn where the integration could not: with
`traceLifecycle: 'static'` the streaming integration is never registered, so an
unwrapped callback previously went unreported.
A `withStaticSpan` callback under `traceLifecycle: 'stream'` no longer downgrades the
lifecycle to `'static'`. Opting out of streaming requires setting
`traceLifecycle: 'static'` explicitly.
Refs #22349
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from 9810912 to 4d63933CompareAugust 4, 2026 13:47
Comment on lines 79 to +82
const processedSpan =
beforeSendSpan && isStreamedBeforeSendSpanCallback(beforeSendSpan)
// check for traceLifecycle here because in static lifecycle,
// captureSpan is called for INP spans. If an unmigrated beforeSendSpan
// callback is run on these spans, it will throw an error.

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.

Bug: In production builds, old beforeSendSpan callbacks expecting v1 SpanJSON format are silently invoked with the new v2 StreamedSpanJSON format, leading to incorrect span processing.
Severity: HIGH

Suggested Fix

Implement a runtime check within captureSpan to prevent v1-style beforeSendSpan callbacks from being invoked with v2 StreamedSpanJSON data in production. Either skip these incompatible callbacks or throw an error to make the breaking change explicit, rather than allowing silent data corruption. The current DEBUG_BUILD warnings are insufficient as they don't run in production.

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/tracing/spans/captureSpan.ts#L79-L82
Potential issue: With the new default `traceLifecycle: 'stream'`, the `captureSpan`
function now invokes `beforeSendSpan` callbacks that are not wrapped with
`withStaticSpan`. If a user has an older callback designed for the v1 `SpanJSON` format
(expecting fields like `description`), it will now be called with the v2
`StreamedSpanJSON` format (with fields like `name` and `end_timestamp`). This mismatch
will cause the callback to silently process incorrect data, leading to failed or
improper span modification. While development builds have warnings, these are stripped
from production builds, making this a silent failure for users who update.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

this is somewhat wrong because we already warn about this and users' callbacks will "break" if they type-check. Another thing we'll fix in a follow-up is to try/catch beforeSendSpan callback invocations, so that the SDK doesn't crash users.

@Lms24
Lms24 merged commit 5e76abe into developAug 4, 2026
266 checks passed
@Lms24
Lms24 deleted the feat/before-send-span-streamed-default branch August 4, 2026 15:34
Sign up for freeto 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.

Make streamed span types the default in public APIs

3 participants

@Lms24@isaacs@chargome
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

feat(core)!: Make beforeSendSpan compatible with streamed spans by default - #22643

Merged
Lms24 merged 13 commits into
developfrom
feat/before-send-span-streamed-default
Aug 4, 2026
Merged

feat(core)!: Make beforeSendSpan compatible with streamed spans by default#22643
Lms24 merged 13 commits into
developfrom
feat/before-send-span-streamed-default

Conversation

@Lms24

@Lms24Lms24 commented Jul 24, 2026

Copy link
Copy Markdown
Member

This PR changes beforeSendSpan to receive StreamedSpanJSON by default, matching the new trace lifecycle default.

Callbacks that intentionally process legacy transaction span JSON need to add the withStaticSpan wrapper that marks the callback as "static"-compatible and hands users the SpanJSON type they used in the callback beforehand.

The withStreamedSpanwrapper remains available as a deprecated compatibility helper and is scheduled for removal in version 12.

More changes:

  • invalid beforeSendSpan callbacks no longer lead to switching the traceLifecycle. Since it now has a default and users need to actively opt out of span streaming, I think it's fair to treat incompatible callbacks as "invalid" and hence skip over them.
  • The beforeSendSpan compatibility checks were moved from the integrations into the core client which ensures that they always run now, even if users selected the static life cycle and hence spanStreamingIntegration doesn't get added.
  • Because we removed sending INP spans as v1 (SpanJSON) spans in favour of always sending them as v2 spans, we now convert a StreamedSpanJson to SpanJson in captureSpan, hand it to the static callback and then convert it back. Not great but I think we need to let users still scrub INP spans.

Closes#22349

@github-actions

github-actionsBot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser29.83 kB+0.13%+37 B 🔺
@sentry/browser - with treeshaking flags28.03 kB+0.05%+14 B 🔺
@sentry/browser (incl. Tracing)47.25 kB+0.09%+42 B 🔺
@sentry/browser (incl. Tracing + Span Streaming)47.26 kB+0.1%+44 B 🔺
@sentry/browser (incl. Tracing, Profiling)51.98 kB+0.09%+43 B 🔺
@sentry/browser (incl. Tracing, Replay)86.58 kB+0.08%+61 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags76 kB+0.06%+42 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas)91.27 kB+0.04%+28 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback)103.92 kB+0.05%+42 B 🔺
@sentry/browser (incl. Feedback)47.13 kB+0.08%+33 B 🔺
@sentry/browser (incl. sendFeedback)34.67 kB+0.08%+27 B 🔺
@sentry/browser (incl. FeedbackAsync)39.78 kB+0.08%+30 B 🔺
@sentry/browser (incl. Metrics)30.91 kB+0.14%+41 B 🔺
@sentry/browser (incl. Logs)31.14 kB+0.13%+39 B 🔺
@sentry/browser (incl. Metrics & Logs)31.81 kB+0.12%+38 B 🔺
@sentry/react31.63 kB+0.12%+36 B 🔺
@sentry/react (incl. Tracing)49.51 kB+0.14%+69 B 🔺
@sentry/vue34.92 kB+0.17%+57 B 🔺
@sentry/vue (incl. Tracing)49.23 kB+0.1%+46 B 🔺
@sentry/svelte29.86 kB+0.13%+36 B 🔺
CDN Bundle31.87 kB+0.01%+1 B 🔺
CDN Bundle (incl. Tracing)47.52 kB+0.01%+1 B 🔺
CDN Bundle (incl. Logs, Metrics)33.41 kB+0.02%+4 B 🔺
CDN Bundle (incl. Tracing, Logs, Metrics)48.93 kB-0.02%-6 B 🔽
CDN Bundle (incl. Replay, Logs, Metrics)72.75 kB-0.01%-2 B 🔽
CDN Bundle (incl. Tracing, Replay)85.18 kB-0.01%-8 B 🔽
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)86.49 kB-0.01%-5 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback)91 kB-0.01%-9 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)92.31 kB-0.02%-10 B 🔽
CDN Bundle - uncompressed94.62 kB-0.09%-84 B 🔽
CDN Bundle (incl. Tracing) - uncompressed142.1 kB-0.05%-57 B 🔽
CDN Bundle (incl. Logs, Metrics) - uncompressed99.24 kB-0.09%-84 B 🔽
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed146.08 kB-0.04%-57 B 🔽
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed223.94 kB-0.04%-84 B 🔽
CDN Bundle (incl. Tracing, Replay) - uncompressed261.36 kB-0.03%-57 B 🔽
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed265.32 kB-0.03%-57 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed275.06 kB-0.03%-57 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed279.01 kB-0.03%-57 B 🔽
@sentry/nextjs (client)52.07 kB+0.12%+61 B 🔺
@sentry/sveltekit (client)47.7 kB+0.14%+62 B 🔺
@sentry/core/server65.52 kB+0.04%+23 B 🔺
@sentry/core/browser51.88 kB+0.09%+43 B 🔺
@sentry/node120.24 kB+0.05%+49 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection)0 Baddedadded
@sentry/node - without tracing83.69 kB+0.06%+50 B 🔺
@sentry/aws-serverless92.35 kB+0.06%+51 B 🔺
@sentry/cloudflare (withSentry) - minified218.65 kB+0.01%+9 B 🔺
@sentry/cloudflare (withSentry)538.85 kB+0.04%+196 B 🔺

View base workflow run

@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from a32a10f to bec33b0CompareJuly 27, 2026 08:41
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from bec33b0 to e303625CompareJuly 27, 2026 14:58
Comment threadpackages/core/src/client.ts
Base automatically changed from feat/span-streaming-default to developJuly 28, 2026 07:36
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch 2 times, most recently from a6f845e to 344055eCompareJuly 28, 2026 08:22
@Lms24Lms24 changed the title feat(core)!: Stream beforeSendSpan payloads by defaultfeat(core)!: Make beforeSendSpan compatible with streamed spans by defaultJul 28, 2026
@Lms24Lms24 self-assigned this Jul 28, 2026
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch 2 times, most recently from 9f59f8c to 9176c4bCompareJuly 29, 2026 15:37
@Lms24
Lms24 marked this pull request as ready for review July 29, 2026 15:42
@Lms24
Lms24 requested review from a team as code ownersJuly 29, 2026 15:42
@Lms24
Lms24 requested review from isaacs, msonnb, mydea, nicohrubec and s1gr1d and removed request for a teamJuly 29, 2026 15:42

@isaacsisaacs left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm not super familiar with this part of the machinery, so leaving it as a comment. If I'm misreading things or someone else disagrees, I'm happy to be overruled :)

Since span streaming is the direction we're moving, and the goal is to make it the default, moving these checks out of the integration and into the client feels like the right call.

Comment threadpackages/core/src/client.ts
Comment threadpackages/core/src/client.ts
Comment threadpackages/core/src/tracing/spans/spanJsonToStreamedSpan.ts Outdated
Comment threadpackages/core/src/tracing/spans/spanJsonToStreamedSpan.ts Outdated
Comment threadpackages/core/src/types/options.ts Outdated
Comment threadpackages/core/test/lib/tracing/spans/captureSpan.test.ts Outdated
Comment threadpackages/core/test/lib/tracing/sentrySpan.test.ts Outdated
Comment threadpackages/core/test/lib/tracing/spans/spanJsonToStreamedSpan.test.ts Outdated
Comment threadMIGRATION.md
Comment threadpackages/core/test/lib/tracing/sentrySpan.test.ts Outdated
Comment threadpackages/core/test/lib/client.test.ts
Lms24 added a commit that referenced this pull request Jul 31, 2026
…` and log warnings if used with streaming (#22908)
This PR:
1. Deprecates `beforeSendTransaction` and `ignoreTransactions`
2. Logs a console warning (intentionally not gated with `debug: true`)
to warn users if they use any of the two options with span streaming
enabled
I intentionally didn't update the migration guide. this is handled in
#22643.
more details in
#22856closes#22856closes#20279
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from 0ec024e to 4910062CompareAugust 3, 2026 12:48
Comment threadpackages/core/src/tracing/sentrySpan.ts Outdated
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from c3ecf63 to 0749d71CompareAugust 3, 2026 14:31
@Lms24
Lms24 marked this pull request as ready for review August 3, 2026 14:48

@chargomechargome left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM just two questions!

Comment threadpackages/browser/src/integrations/spanstreaming.ts
Comment threadpackages/core/src/tracing/spans/captureSpan.ts Outdated
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from 9ef7183 to f6cfc05CompareAugust 4, 2026 09:23

@cursorcursorBot 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 f6cfc05. Configure here.

Comment threadpackages/core/test/lib/tracing/spans/captureSpan.test.ts
Comment threadpackages/core/src/client.ts
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from 9dcfb44 to 6fb09a1CompareAugust 4, 2026 13:33
Lms24and others added 13 commits August 4, 2026 15:47
Make streamed span JSON the default beforeSendSpan contract and provide
withStaticSpan for callbacks that still process transaction span JSON.
Deprecate withStreamedSpan for removal in version 12.
BREAKING CHANGE: beforeSendSpan receives StreamedSpanJSON by default.
Fixes#22349
Co-Authored-By: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
`withStreamedSpan` no longer marks its callback. Nothing reads `_streamed` since
`isStreamedBeforeSendSpanCallback` became "not wrapped with `withStaticSpan`", so the
wrapper returns the callback unchanged instead of mutating a function it was handed.
Resolve `traceLifecycle` once in the `Client` constructor, where any value other than
`'static'` normalizes to the `'stream'` default. Neither span streaming integration
writes back to the option anymore, which removes an ordering hazard: integrations that
read `hasSpanStreamingEnabled` during their own `setup` could observe the value before
or after the mutation depending on integration order. The registration gates now test
`!== 'static'` so they agree with the normalized value; previously an unknown value
failed the gate but still resolved to `'stream'`, leaving nothing to flush spans.
Move the `beforeSendSpan` format check into the constructor as well. A callback is only
invoked for the span format matching the trace lifecycle, so a mismatch means it is
never called. The client can warn where the integration could not: with
`traceLifecycle: 'static'` the streaming integration is never registered, so an
unwrapped callback previously went unreported.
A `withStaticSpan` callback under `traceLifecycle: 'stream'` no longer downgrades the
lifecycle to `'static'`. Opting out of streaming requires setting
`traceLifecycle: 'static'` explicitly.
Refs #22349
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Lms24
Lms24force-pushed the feat/before-send-span-streamed-default branch from 9810912 to 4d63933CompareAugust 4, 2026 13:47
Comment on lines 79 to +82
const processedSpan =
beforeSendSpan && isStreamedBeforeSendSpanCallback(beforeSendSpan)
// check for traceLifecycle here because in static lifecycle,
// captureSpan is called for INP spans. If an unmigrated beforeSendSpan
// callback is run on these spans, it will throw an error.

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.

Bug: In production builds, old beforeSendSpan callbacks expecting v1 SpanJSON format are silently invoked with the new v2 StreamedSpanJSON format, leading to incorrect span processing.
Severity: HIGH

Suggested Fix

Implement a runtime check within captureSpan to prevent v1-style beforeSendSpan callbacks from being invoked with v2 StreamedSpanJSON data in production. Either skip these incompatible callbacks or throw an error to make the breaking change explicit, rather than allowing silent data corruption. The current DEBUG_BUILD warnings are insufficient as they don't run in production.

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/tracing/spans/captureSpan.ts#L79-L82
Potential issue: With the new default `traceLifecycle: 'stream'`, the `captureSpan`
function now invokes `beforeSendSpan` callbacks that are not wrapped with
`withStaticSpan`. If a user has an older callback designed for the v1 `SpanJSON` format
(expecting fields like `description`), it will now be called with the v2
`StreamedSpanJSON` format (with fields like `name` and `end_timestamp`). This mismatch
will cause the callback to silently process incorrect data, leading to failed or
improper span modification. While development builds have warnings, these are stripped
from production builds, making this a silent failure for users who update.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

this is somewhat wrong because we already warn about this and users' callbacks will "break" if they type-check. Another thing we'll fix in a follow-up is to try/catch beforeSendSpan callback invocations, so that the SDK doesn't crash users.

@Lms24
Lms24 merged commit 5e76abe into developAug 4, 2026
266 checks passed
@Lms24
Lms24 deleted the feat/before-send-span-streamed-default branch August 4, 2026 15:34
Sign up for freeto 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.

Make streamed span types the default in public APIs

3 participants

@Lms24@isaacs@chargome