can1357/oh-my-pi · warning · AbortError

AbortError

Error message

AbortError

What it means

This AIError.AbortError is thrown when the Anthropic stream ends without any events while the caller had aborted the request. After a stream-termination condition the library first checks local timeout aborts and then whether the caller aborted, and surfaces a clean AbortError instead of a generic stream error.

Source

Thrown at packages/ai/src/providers/anthropic.ts:2659

									}
								} else {
									calculateCost(model, output.usage);
								}
							}
						} else if (event.type === "message_stop") {
							sawTerminalEnvelope = true;
							sawMessageStop = true;
							// The protocol is complete even if a broken keep-alive leaves the HTTP body open.
							break;
						}
					}

					const firstEventTimeoutError = activeAbortTracker.getLocalAbortReason();
					if (firstEventTimeoutError) {
						throw firstEventTimeoutError;
					}
					if (activeAbortTracker.wasCallerAbort()) {
						throw new AIError.AbortError();
					}
					if (!sawEvent || !sawMessageStart) {
						throw new AIError.AnthropicStreamEnvelopeError("stream ended before message_start");
					}
					if (!sawTerminalEnvelope) {
						// Neither a message_delta stop_reason nor message_stop arrived: the
						// connection died mid-generation. Finalizing the partial message as
						// a clean "stop" would make the agent loop treat the truncated turn
						// as complete (silent mid-sentence halt), so fail the turn. The
						// envelope error is transparently retried before replay-unsafe
						// content streams; afterwards it surfaces as an error turn whose
						// complete tool calls the agent loop salvages
						// (`recoverTransientErrorToolTurn` recognizes the envelope-error
						// text and `retainCompletedToolCalls` drops half-streamed calls).
						throw new AIError.AnthropicStreamEnvelopeError("stream ended before message_stop");
					}
					if (!sawMessageStop) {
						// A stop_reason arrived via message_delta, so generation finished;

View on GitHub (pinned to 9690622007)

Solutions

  1. Expected behavior on cancellation — handle AbortError distinctly from real failures and treat it as a cancelled turn.
  2. If unintended, increase or remove the AbortSignal timeout so the model has time to emit the first event.
  3. Check code paths that abort the signal (UI cancel buttons, orchestration timeouts) if cancellation was not expected.
  4. Retry the request if the abort was caused by an aggressive deadline; consider streaming-first-token timeouts instead.

Example fix

// before
const res = await streamAnthropic({ prompt }); // no signal, random teardown aborts

// after
const controller = new AbortController();
try {
  const res = await streamAnthropic({ prompt, signal: controller.signal });
} catch (err) {
  if (err instanceof AIError.AbortError) return; // cancelled, not a failure
  throw err;
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (signal?.aborted) {
  return; // don't start a stream that will immediately be aborted
}

Type guard

function isAbortError(err: unknown): err is AIError.AbortError {
  return err instanceof AIError.AbortError || (err instanceof Error && err.name === "AbortError");
}

Try / catch

try {
  for await (const event of stream) {
    handle(event);
  }
} catch (err) {
  if (err instanceof AIError.AbortError) {
    return; // cancellation is expected, not a failure
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: The caller's AbortSignal fired (activeAbortTracker.wasCallerAbort() is true) and the SSE stream then terminated before any event was processed — e.g. user cancellation, request timeout, or application shutdown during a streaming Anthropic call with no first event received.

Common situations: User cancels a generation in the UI; a deadline/AbortSignal.timeout() elapses before the model responds; process shutdown aborts in-flight streams; slow first token causing the caller to abort.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/6ba743a142d61dc9. Report an issue: GitHub.