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

@Lms24 Lms24 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-actions Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

Path Size % Change Change
@sentry/browser 29.83 kB +0.13% +37 B 🔺
@sentry/browser - with treeshaking flags 28.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 flags 76 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/react 31.63 kB +0.12% +36 B 🔺
@sentry/react (incl. Tracing) 49.51 kB +0.14% +69 B 🔺
@sentry/vue 34.92 kB +0.17% +57 B 🔺
@sentry/vue (incl. Tracing) 49.23 kB +0.1% +46 B 🔺
@sentry/svelte 29.86 kB +0.13% +36 B 🔺
CDN Bundle 31.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 - uncompressed 94.62 kB -0.09% -84 B 🔽
CDN Bundle (incl. Tracing) - uncompressed 142.1 kB -0.05% -57 B 🔽
CDN Bundle (incl. Logs, Metrics) - uncompressed 99.24 kB -0.09% -84 B 🔽
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 146.08 kB -0.04% -57 B 🔽
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 223.94 kB -0.04% -84 B 🔽
CDN Bundle (incl. Tracing, Replay) - uncompressed 261.36 kB -0.03% -57 B 🔽
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 265.32 kB -0.03% -57 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 275.06 kB -0.03% -57 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 279.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/server 65.52 kB +0.04% +23 B 🔺
@sentry/core/browser 51.88 kB +0.09% +43 B 🔺
@sentry/node 120.24 kB +0.05% +49 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection) 0 B added added
@sentry/node - without tracing 83.69 kB +0.06% +50 B 🔺
@sentry/aws-serverless 92.35 kB +0.06% +51 B 🔺
@sentry/cloudflare (withSentry) - minified 218.65 kB +0.01% +9 B 🔺
@sentry/cloudflare (withSentry) 538.85 kB +0.04% +196 B 🔺

View base workflow run

@Lms24
Lms24 force-pushed the feat/before-send-span-streamed-default branch from a32a10f to bec33b0 Compare July 27, 2026 08:41
@Lms24
Lms24 force-pushed the feat/before-send-span-streamed-default branch from bec33b0 to e303625 Compare July 27, 2026 14:58
Comment thread packages/core/src/client.ts
Base automatically changed from feat/span-streaming-default to develop July 28, 2026 07:36
@Lms24
Lms24 force-pushed the feat/before-send-span-streamed-default branch 2 times, most recently from a6f845e to 344055e Compare July 28, 2026 08:22
@Lms24 Lms24 changed the title feat(core)!: Stream beforeSendSpan payloads by default feat(core)!: Make beforeSendSpan compatible with streamed spans by default Jul 28, 2026
@Lms24 Lms24 self-assigned this Jul 28, 2026
@Lms24
Lms24 force-pushed the feat/before-send-span-streamed-default branch 2 times, most recently from 9f59f8c to 9176c4b Compare July 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 owners July 29, 2026 15:42
@Lms24
Lms24 requested review from isaacs, msonnb, mydea, nicohrubec and s1gr1d and removed request for a team July 29, 2026 15:42

@isaacs isaacs 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 thread packages/core/src/client.ts
Comment thread packages/core/src/client.ts
Comment thread packages/core/src/tracing/spans/spanJsonToStreamedSpan.ts Outdated
Comment thread packages/core/src/tracing/spans/spanJsonToStreamedSpan.ts Outdated
Comment thread packages/core/src/types/options.ts Outdated
Comment thread packages/core/test/lib/tracing/spans/captureSpan.test.ts Outdated
Comment thread packages/core/test/lib/tracing/sentrySpan.test.ts Outdated
Comment thread packages/core/test/lib/tracing/spans/spanJsonToStreamedSpan.test.ts Outdated
Comment thread MIGRATION.md
Comment thread packages/core/test/lib/tracing/sentrySpan.test.ts Outdated
Comment thread packages/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
#22856

closes #22856
closes #20279
@Lms24
Lms24 force-pushed the feat/before-send-span-streamed-default branch from 0ec024e to 4910062 Compare August 3, 2026 12:48
Comment thread packages/core/src/tracing/sentrySpan.ts Outdated
@Lms24
Lms24 force-pushed the feat/before-send-span-streamed-default branch from c3ecf63 to 0749d71 Compare August 3, 2026 14:31
@Lms24
Lms24 marked this pull request as ready for review August 3, 2026 14:48
@Lms24
Lms24 requested review from andreiborza and chargome August 3, 2026 15:31

@chargome chargome 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 thread packages/browser/src/integrations/spanstreaming.ts
Comment thread packages/core/src/tracing/spans/captureSpan.ts Outdated
@Lms24
Lms24 force-pushed the feat/before-send-span-streamed-default branch from 9ef7183 to f6cfc05 Compare August 4, 2026 09:23

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

Fix All in Cursor

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

Reviewed by Cursor Bugbot for commit f6cfc05. Configure here.

Comment thread packages/core/test/lib/tracing/spans/captureSpan.test.ts
Comment thread packages/core/src/client.ts
@Lms24
Lms24 force-pushed the feat/before-send-span-streamed-default branch from 9dcfb44 to 6fb09a1 Compare August 4, 2026 13:33
Lms24 and 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
Lms24 force-pushed the feat/before-send-span-streamed-default branch from 9810912 to 4d63933 Compare August 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
Member Author

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 develop Aug 4, 2026
266 checks passed
@Lms24
Lms24 deleted the feat/before-send-span-streamed-default branch August 4, 2026 15:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make streamed span types the default in public APIs

3 participants