Skip to content

fix(server-utils): Surface deferred span-end errors on the channel payload - #24540

Merged
Lms24 merged 5 commits into
developfrom
fix/vercel-ai-aborted-stream-unhandled-rejection
Sep 22, 2026
Merged

Lms24 merged 5 commits into
developfrom
fix/vercel-ai-aborted-stream-unhandled-rejection

Conversation

@Lms24

@Lms24 Lms24 commented Sep 21, 2026

Copy link
Copy Markdown
Member

This PR fixes a bug where an abort error thrown in a span with a deferred end, would cause unhandled rejections. This happened because we didn't properly attach the original error was thrown to the data object from the tracing channel. We'd only update the span and its status but the Vercel AI instrumentation listening to beforeSpanEnd wouldn't go into the 'error' in data branch due to the missing error object.

This PR now attaches the error object or message and adds a regression test covering the scenario reported in #24532

Reviewers, this is my first time doing basically anything at tracing channel level. Please let me know if this should be handled differently.

Fixes #24532

…yload

An aborted `streamText` left up to 7 unhandled rejections per abort. The deferred
end's `end(error)` annotated the span but never marked the payload, so
`beforeSpanEnd` took the success branch and enriched the span from the raw
`StreamTextResult` — whose `usage`/`response`/`providerMetadata`/`text`/… are
getters that derive a *fresh* promise from the already-rejected stream on every
read, each one leaking an unhandled rejection.

`AbortError` reasons are suppressed by name in `onUnhandledRejectionIntegration`,
but an abort reason is whatever the caller passes (`@hono/node-server` passes a
string), so the ignore list can't cover this.

Fixes #24532

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

Path Size % Change Change
@sentry/browser 29.07 kB - -
@sentry/browser - with treeshaking flags 27.34 kB - -
@sentry/browser - with treeshaking flags tracing without tracing 27.24 kB - -
@sentry/browser (incl. Tracing) 50.55 kB - -
@sentry/browser (incl. Tracing + Span Streaming) 50.56 kB - -
@sentry/browser (incl. Tracing, Profiling) 53.55 kB - -
@sentry/browser (incl. Tracing, Replay) 90.09 kB - -
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 79.18 kB - -
@sentry/browser (incl. Tracing, Replay with Canvas) 94.78 kB - -
@sentry/browser (incl. Tracing, Replay, Feedback) 107.76 kB - -
@sentry/browser (incl. Feedback) 46.6 kB - -
@sentry/browser (incl. sendFeedback) 34.13 kB - -
@sentry/browser (incl. FeedbackAsync) 39.25 kB - -
@sentry/browser (incl. Metrics) 30.08 kB - -
@sentry/browser (incl. Logs) 30.34 kB - -
@sentry/browser (incl. Metrics & Logs) 31 kB - -
@sentry/react 30.82 kB - -
@sentry/react (incl. Tracing) 52.86 kB - -
@sentry/vue 36.33 kB - -
@sentry/vue (incl. Tracing) 52.84 kB - -
@sentry/svelte 29.1 kB - -
CDN Bundle 30.77 kB - -
CDN Bundle (incl. Tracing) 51.06 kB - -
CDN Bundle (incl. Logs, Metrics) 33.02 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) 53.05 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) 73.71 kB - -
CDN Bundle (incl. Tracing, Replay) 88.61 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 90.54 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) 94.66 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 96.67 kB - -
CDN Bundle - uncompressed 91.07 kB - -
CDN Bundle (incl. Tracing) - uncompressed 152.44 kB - -
CDN Bundle (incl. Logs, Metrics) - uncompressed 97.64 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 158.4 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 227.05 kB - -
CDN Bundle (incl. Tracing, Replay) - uncompressed 272.01 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 277.95 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 285.72 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 291.65 kB - -
@sentry/nextjs (client) 55.19 kB - -
@sentry/sveltekit (client) 50.98 kB - -
@sentry/core/server 39.97 kB - -
@sentry/core/browser 13.63 kB - -
@sentry/node 133.38 kB +0.03% +28 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection) 82.15 kB - -
@sentry/node - without tracing 89.93 kB +0.04% +27 B 🔺
@sentry/node - without channel injection 112.17 kB +0.03% +32 B 🔺
@sentry/aws-serverless 98.2 kB +0.03% +23 B 🔺
@sentry/cloudflare (withSentry) - minified 204.67 kB - -
@sentry/cloudflare (withSentry) 509.34 kB - -

View base workflow run

@Lms24

Lms24 commented Sep 21, 2026

Copy link
Copy Markdown
Member Author

bugbot review

@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.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit de5fc51. Configure here.

Lms24 and others added 4 commits September 21, 2026 14:16
The abort path had no coverage at all, so nothing recorded that enrichment used
to backfill `gen_ai.response.model` from the request's model id on a span whose
model never responded.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Removes the cast in the deferred end: the field is part of the payload's real
shape (Node writes it, and every channel context declares it), it was just
missing from `TracingChannelPayloadWithSpan`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Aborting from inside `doStream` hits the same pre-first-chunk race without a
timer, and the trailing sleep was not load-bearing — the scenario still fails
pre-fix without it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@logaretm logaretm 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.

Nice fix!

@Lms24
Lms24 merged commit e910cca into develop Sep 22, 2026
325 checks passed
@Lms24
Lms24 deleted the fix/vercel-ai-aborted-stream-unhandled-rejection branch September 22, 2026 08:10
Lms24 added a commit that referenced this pull request Sep 22, 2026
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.

vercelAIIntegration: aborted streamText leaves unhandled rejections when the abort reason isn't an AbortError (10.75.0; 7 per abort on 11.0.0-rc.0)

2 participants