Skip to content

fix(vercelai): Avoid double-capturing v4 tool errors in orchestrion mode#22293

Open
mydea wants to merge 3 commits into
developfrom
fix/vercelai-tool-error-express-flake
Open

fix(vercelai): Avoid double-capturing v4 tool errors in orchestrion mode#22293
mydea wants to merge 3 commits into
developfrom
fix/vercelai-tool-error-express-flake

Conversation

@mydea

@mydea mydea commented Jul 15, 2026

Copy link
Copy Markdown
Member

In orchestrion mode, a v4 Vercel AI tool error was reported to Sentry twice: once by the channel subscriber (captureToolError, which captured the raw error mid-execution) and again when the SDK's wrapped AI_ToolExecutionError bubbled up to the app's error handling (express handler, or the global unhandled-rejection handler). The two events are different objects (raw error vs. wrapper), so Sentry's already-captured dedup didn't collapse them.

This also caused a flaky test: captures error in tool in express server used .unordered() with a single event expect and matched whichever of the two error envelopes arrived first — when the express-handler envelope won the race, the tool-tag assertions failed.

Root cause

The channel subscriber's tool-execute wrapper always captured the thrown error and re-threw. That capture is required on v5, where executeTools swallows the rejection into tool-error content so it never surfaces otherwise. On v4 the rejection instead bubbles out of the ai call, so it is already captured by the app's error handling — making the channel capture a duplicate. The OTel integration never had this problem because it doesn't self-capture v4 thrown tool errors; it lets them bubble.

Changes:

  • The orchestrion subscriber now only self-captures tool errors when the enclosing operation swallows them (v5+, detected via the 'v1' model spec version already used elsewhere in the file). On v4 it marks the tool span as errored and lets the error bubble, matching the OTel path — so a single error event is reported.
  • To preserve trace correlation for bubbled errors, the subscriber stamps the operation's call-site span onto the error via _sentry_active_span, reusing the exact mechanism the OTel integration already uses (onunhandledrejection restores it at capture time). Without this, an unhandled rejection in a non-OTel (orchestrion) process would start its own trace instead of correlating to the transaction.
  • Both of the above are per-operation facts keyed by the operation span, so they're stored together in a single WeakMap<Span, OperationErrorInfo> ({ callSiteSpan, toolErrorsBubbleToCaller }) written once at operation start, rather than two parallel collections.
  • Tests updated: both v4 error-in-tool scenarios now assert a single AI_ToolExecutionError event correlated to the transaction, in both orchestrion and OTel modes.

Fixes the flaky suites/tracing/vercelai/test.ts > ... > captures error in tool in express server [cjs].

mydea and others added 2 commits July 15, 2026 14:49
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mydea mydea changed the title test(node): Fix flaky vercelai tool-error express test fix(vercelai): Avoid double-capturing v4 tool errors in orchestrion mode Jul 15, 2026
@github-actions

github-actions Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

Path Size % Change Change
@sentry/browser 27.67 kB - -
@sentry/browser - with treeshaking flags 26.12 kB - -
@sentry/browser (incl. Tracing) 46.44 kB - -
@sentry/browser (incl. Tracing + Span Streaming) 48.23 kB - -
@sentry/browser (incl. Tracing, Profiling) 51.23 kB - -
@sentry/browser (incl. Tracing, Replay) 85.69 kB - -
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 75.33 kB - -
@sentry/browser (incl. Tracing, Replay with Canvas) 90.41 kB - -
@sentry/browser (incl. Tracing, Replay, Feedback) 103.07 kB - -
@sentry/browser (incl. Feedback) 44.85 kB - -
@sentry/browser (incl. sendFeedback) 32.47 kB - -
@sentry/browser (incl. FeedbackAsync) 37.6 kB - -
@sentry/browser (incl. Metrics) 28.77 kB - -
@sentry/browser (incl. Logs) 29 kB - -
@sentry/browser (incl. Metrics & Logs) 29.68 kB - -
@sentry/react 29.48 kB - -
@sentry/react (incl. Tracing) 48.7 kB - -
@sentry/vue 33.1 kB - -
@sentry/vue (incl. Tracing) 48.42 kB - -
@sentry/svelte 27.7 kB - -
CDN Bundle 30.07 kB - -
CDN Bundle (incl. Tracing) 48.43 kB - -
CDN Bundle (incl. Logs, Metrics) 31.65 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) 49.73 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) 70.89 kB - -
CDN Bundle (incl. Tracing, Replay) 85.93 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 87.23 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) 91.72 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 92.98 kB - -
CDN Bundle - uncompressed 89.7 kB - -
CDN Bundle (incl. Tracing) - uncompressed 146.45 kB - -
CDN Bundle (incl. Logs, Metrics) - uncompressed 94.4 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 150.42 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 219.13 kB - -
CDN Bundle (incl. Tracing, Replay) - uncompressed 265.65 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 269.61 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 279.35 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 283.3 kB - -
@sentry/nextjs (client) 51.25 kB - -
@sentry/sveltekit (client) 46.88 kB - -
@sentry/core/server 78.54 kB - -
@sentry/core/browser 64.89 kB - -
@sentry/node-core 62.83 kB - -
@sentry/node 125.25 kB - -
@sentry/node (incl. diagnostics channel injection) 140.37 kB +0.07% +95 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection) 69.96 kB - -
@sentry/node/light 50.84 kB - -
@sentry/node - without tracing 74.21 kB - -
@sentry/aws-serverless 83.42 kB +0.01% +4 B 🔺
@sentry/cloudflare (withSentry) - minified 181.91 kB - -
@sentry/cloudflare (withSentry) 450.25 kB - -

View base workflow run

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mydea mydea marked this pull request as ready for review July 15, 2026 14:03
@mydea mydea requested review from a team as code owners July 15, 2026 14:03
@mydea mydea requested review from JPeer264, andreiborza and isaacs and removed request for a team July 15, 2026 14:03
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.

1 participant