Skip to content

fix(ai): propagate AbortError in generateText multi-step tool loop#12915

Open
sleitor wants to merge 1 commit intovercel:mainfrom
sleitor:fix/generate-text-abort-signal-propagation
Open

fix(ai): propagate AbortError in generateText multi-step tool loop#12915
sleitor wants to merge 1 commit intovercel:mainfrom
sleitor:fix/generate-text-abort-signal-propagation

Conversation

@sleitor
Copy link
Copy Markdown
Contributor

@sleitor sleitor commented Feb 27, 2026

Summary

Fixes #12878

When an AbortSignal fires between steps in a multi-step tool loop (e.g. during tool execution), some providers return a partial result with finishReason: 'other' instead of throwing. The generateText function silently returned the partial result with no error, giving callers no indication the generation was interrupted.

Root Cause

The do { ... } while (...) loop in generate-text.ts passes the mergedAbortSignal to each model call and tool execution, but it never checks whether the signal was already aborted between iterations. If a provider happens to return rather than throw on an aborted request, the loop continues into the next step as if nothing happened.

Fix

Add an abort guard at the top of each loop iteration:

do {
  // Check if the abort signal was triggered between steps.
  if (mergedAbortSignal?.aborted) {
    throw (
      mergedAbortSignal.reason ??
      new DOMException('This operation was aborted', 'AbortError')
    );
  }
  // ... rest of step
} while (...)

This ensures that regardless of how the provider handled the abort, generateText always throws an AbortError when the signal is fired — consistent with standard Web API abort semantics.

Test

Added a test that:

  1. Starts a multi-step generation (model returns a tool call on step 1)
  2. Aborts the signal inside the tool's execute function
  3. Asserts that generateText throws an AbortError and the second model call is never made

Also updated the existing should forward abort signal to tool execution test: its previous pattern (aborting externally then await generateTextPromise) relied on the function silently absorbing the abort, which is the bug we're fixing. The revised test simply verifies the abort signal is forwarded to the tool execute callback, which is its actual intent.

@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 Feb 27, 2026
When an AbortSignal fires between steps (e.g. during tool execution),
some providers return a partial result with finishReason:'other/unknown'
instead of throwing. The multi-step do-while loop now checks
mergedAbortSignal.aborted at the start of each iteration and re-throws
the signal reason, ensuring callers reliably receive an AbortError rather
than a silent partial result.

Fixes vercel#12878
@sleitor sleitor force-pushed the fix/generate-text-abort-signal-propagation branch from 0ef3389 to d57e70e Compare March 22, 2026 01:31
@sleitor
Copy link
Copy Markdown
Contributor Author

sleitor commented Apr 7, 2026

👋 Gentle ping — just checking if this is still on the radar for review. Happy to address any feedback or rebase if needed!

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