vercel/ai · info · DOMException
AbortError
AbortError
Error message
Polling was aborted
What it means
pollGoogleInteractionUntilTerminal polls GET /interactions/{id} until a background Google interaction reaches a terminal status. Before each tick it checks the caller-supplied AbortSignal; if it is already aborted it first fires a best-effort POST /interactions/{id}/cancel so the run stops billing server-side, then throws this AbortError. It is the library's way of honoring cancellation of a long-running background interaction poll.
Source
Thrown at packages/google/src/interactions/poll-google-interactions.ts:91
const startedAt = Date.now();
let nextDelayMs = initialDelayMs;
const url = `${baseURL}/interactions/${encodeURIComponent(interactionId)}`;
/*
* When the caller aborts, fire a best-effort `POST /interactions/{id}/cancel`
* so the run stops billing on Google's side. Wrap every exit path that's
* triggered by an abort -- the explicit `abortSignal.aborted` check, the
* AbortError thrown by `delay()`, and any AbortError thrown by `getFromApi`.
*/
const cancelOnServer = () =>
cancelGoogleInteraction({ baseURL, interactionId, headers, fetch });
try {
while (true) {
if (abortSignal?.aborted) {
await cancelOnServer();
throw new DOMException('Polling was aborted', 'AbortError');
}
if (Date.now() - startedAt > timeoutMs) {
throw new Error(
`google.interactions: timed out polling interaction ${interactionId} after ${timeoutMs}ms.`,
);
}
await delay(nextDelayMs, { abortSignal });
const {
value: response,
rawValue: rawResponse,
responseHeaders,
} = await getFromApi({
url,
validateUrl: false,
headers,View on GitHub (pinned to 69428b1f8b)
Solutions
- Catch AbortError (or use error.name === 'AbortError') and treat the interaction as cancelled; the library already sent a server-side cancel.
- Increase or restructure the abortSignal lifetime (e.g. use AbortSignal.any with a longer timeout) if cancellation was unintentional.
- Pass providerOptions.google.pollingTimeoutMs to raise pollingTimeoutMs so the poll is not cut short before finishing.
- Re-poll later by calling the interactions API with the same interactionId if you still need the result.
Example fix
// before
const result = await generateText({ model, ... });
// after
try {
const result = await generateText({ model, abortSignal: controller.signal, ... });
} catch (error) {
if (error instanceof Error && error.name === 'AbortError') {
// interaction was cancelled server-side; handle gracefully
} else {
throw error;
}
} Defensive patterns
Strategy: try-catch
Validate before calling
if (abortSignal?.aborted) { /* skip the call entirely */ } Try / catch
try {
await pollOrGenerate({ abortSignal });
} catch (error) {
if (error instanceof DOMException && error.name === 'AbortError') {
// interaction was cancelled server-side; return a graceful response
} else { throw error; }
} Prevention
- Pass an abortSignal only when cancellation is truly desired; use AbortSignal.timeout sized to the expected run duration.
- Remember the library sends a server-side cancel on abort — do not also cancel manually.
- Account for deep-research interactions taking tens of minutes when sizing signals.
When it happens
Trigger: Calling generateText/streamText (or the interactions API directly) against a background Google interaction while passing an abortSignal, and that signal fires (or is already aborted) while pollGoogleInteractionUntilTerminal is still waiting for a terminal status.
Common situations: User cancels a request in a server handler (req.signal / AbortController.timeout), a client disconnects mid-run, or a timeout AbortSignal fires because a deep-research/agent interaction takes many minutes.
Related errors
- Transcription request was aborted
- Stream aborted
- google.interactions: timed out polling interaction ${interac
- MCP client initialization was aborted
- AbortError
AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30).
Data as JSON: /api/errors/e1bd9a61f82e236a.
Report an issue: GitHub.