Skip to content

fix: re-throw AbortError in generateText multi-step tool loop#13205

Open
MaxwellCalkin wants to merge 1 commit intovercel:mainfrom
MaxwellCalkin:fix/generate-text-abort-error-swallowed
Open

fix: re-throw AbortError in generateText multi-step tool loop#13205
MaxwellCalkin wants to merge 1 commit intovercel:mainfrom
MaxwellCalkin:fix/generate-text-abort-error-swallowed

Conversation

@MaxwellCalkin
Copy link

Note: This PR was authored by Claude (AI), operated by @MaxwellCalkin.

Fixes #12878

Summary

generateText() silently swallows AbortError when an AbortSignal fires mid-generation in a multi-step tool-use loop. Instead of throwing, it returns a normal result, and the caller has no indication the generation was interrupted.

Root cause

In executeToolCall(), the catch (error) block catches all errors — including AbortError — and converts them into tool-error results. This means when a tool's execute function is aborted (or when an abort happens during tool execution), the error is swallowed and returned as a normal tool-error result. The do...while loop in generateText() then either continues (and may hit an AbortError on the next doGenerate() call) or terminates normally if the while condition evaluates to false.

Changes

  1. packages/ai/src/generate-text/execute-tool-call.ts: Added isAbortError check at the top of the catch block. If the error is an AbortError, it is recorded on the telemetry span and re-thrown immediately, rather than being converted to a tool-error result. This is consistent with how the retry logic in retry-with-exponential-backoff.ts already handles AbortErrors (line 98: if (isAbortError(error)) { throw error; }).

  2. packages/ai/src/generate-text/generate-text.ts: Added mergedAbortSignal?.aborted check at the start of each do...while loop iteration. This catches the edge case where the signal fires between steps — after tool execution completes but before the next doGenerate() call would detect it.

Test plan

  • Verify existing tests pass (the abort re-throw should not affect normal tool error handling)
  • Reproduce the issue from generateText() silently swallows AbortError in multi-step tool loop #12878: call generateText() with an AbortSignal that fires during tool execution in a multi-step loop, and confirm it now throws an AbortError instead of returning normally
  • Verify that non-abort tool errors (e.g., tool throws a regular Error) are still converted to tool-error results as before

Fixes vercel#12878.

generateText() silently swallows AbortError when an AbortSignal fires
mid-generation in a multi-step tool-use loop. The root cause is in
executeToolCall(), which catches ALL errors (including AbortError) and
converts them into tool-error results. This means the abort is never
propagated to the caller.

Changes:
- executeToolCall.ts: Check for AbortError before converting to
  tool-error result. If the error is an AbortError, record it on the
  span and re-throw so it propagates correctly.
- generate-text.ts: Check mergedAbortSignal.aborted at the start of
  each loop iteration. This catches the edge case where the signal
  fires between steps (after tool execution completes but before the
  next doGenerate() call).
@tigent tigent bot added ai/core core functions like generateText, streamText, etc. Provider utils, and provider spec. bug Something isn't working as documented labels Mar 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai/core core functions like generateText, streamText, etc. Provider utils, and provider spec. bug Something isn't working as documented

Projects

None yet

Development

Successfully merging this pull request may close these issues.

generateText() silently swallows AbortError in multi-step tool loop

1 participant