Commit 38d0d7b
fix(vercelai): Avoid double-capturing v4 tool errors in orchestrion mode (#22293)
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]`.
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>1 parent 27b350f commit 38d0d7b
2 files changed
Lines changed: 84 additions & 53 deletions
File tree
- dev-packages/node-integration-tests/suites/tracing/vercelai
- packages/server-utils/src/vercel-ai
Lines changed: 28 additions & 40 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
321 | 321 | | |
322 | 322 | | |
323 | 323 | | |
324 | | - | |
325 | | - | |
326 | 324 | | |
327 | | - | |
328 | | - | |
329 | | - | |
330 | | - | |
331 | | - | |
332 | | - | |
333 | | - | |
334 | | - | |
335 | | - | |
336 | | - | |
337 | | - | |
338 | | - | |
339 | | - | |
340 | | - | |
341 | | - | |
342 | | - | |
343 | | - | |
344 | | - | |
345 | | - | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
346 | 340 | | |
347 | 341 | | |
348 | 342 | | |
| |||
352 | 346 | | |
353 | 347 | | |
354 | 348 | | |
355 | | - | |
| 349 | + | |
356 | 350 | | |
357 | 351 | | |
358 | 352 | | |
| |||
403 | 397 | | |
404 | 398 | | |
405 | 399 | | |
406 | | - | |
407 | | - | |
408 | | - | |
409 | | - | |
410 | | - | |
411 | | - | |
412 | | - | |
413 | | - | |
414 | | - | |
415 | | - | |
416 | | - | |
417 | | - | |
418 | | - | |
419 | | - | |
420 | | - | |
421 | | - | |
422 | | - | |
423 | | - | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
424 | 412 | | |
425 | 413 | | |
426 | 414 | | |
| |||
Lines changed: 56 additions & 13 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | | - | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
4 | 11 | | |
5 | 12 | | |
6 | 13 | | |
| |||
100 | 107 | | |
101 | 108 | | |
102 | 109 | | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
103 | 125 | | |
104 | 126 | | |
105 | 127 | | |
| |||
220 | 242 | | |
221 | 243 | | |
222 | 244 | | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
223 | 248 | | |
224 | 249 | | |
225 | 250 | | |
226 | 251 | | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
227 | 256 | | |
228 | 257 | | |
229 | 258 | | |
| |||
239 | 268 | | |
240 | 269 | | |
241 | 270 | | |
242 | | - | |
243 | | - | |
244 | | - | |
245 | | - | |
246 | | - | |
247 | | - | |
248 | | - | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
249 | 277 | | |
250 | 278 | | |
251 | 279 | | |
| |||
262 | 290 | | |
263 | 291 | | |
264 | 292 | | |
265 | | - | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
266 | 303 | | |
267 | 304 | | |
268 | 305 | | |
| |||
586 | 623 | | |
587 | 624 | | |
588 | 625 | | |
589 | | - | |
590 | | - | |
591 | | - | |
| 626 | + | |
| 627 | + | |
| 628 | + | |
| 629 | + | |
| 630 | + | |
592 | 631 | | |
593 | | - | |
| 632 | + | |
| 633 | + | |
| 634 | + | |
| 635 | + | |
| 636 | + | |
594 | 637 | | |
595 | 638 | | |
596 | 639 | | |
| |||
0 commit comments