can1357/oh-my-pi · warning · AIError.AbortError
Request aborted
Error message
Request aborted
What it means
buildGoogleGenerateContentParams throws AIError.AbortError("Request aborted") when the AbortSignal passed in options.signal is already aborted at request-build time. The library checks the signal before sending so an already-cancelled request never reaches the network.
Source
Thrown at packages/ai/src/providers/google-shared.ts:874
const thinking = options.thinking;
if (
thinking &&
model.reasoning &&
(thinking.enabled || thinking.level !== undefined || thinking.budgetTokens !== undefined)
) {
const cfg: ThinkingConfig = { includeThoughts: thinking.enabled && !options.hideThinkingSummary };
if (thinking.level !== undefined) {
// GoogleThinkingLevel mirrors the SDK's ThinkingLevel string enum values 1:1.
cfg.thinkingLevel = thinking.level as ThinkingLevel;
} else if (thinking.budgetTokens !== undefined) {
cfg.thinkingBudget = thinking.budgetTokens;
}
config.thinkingConfig = cfg;
}
if (options.signal) {
if (options.signal.aborted) {
throw new AIError.AbortError("Request aborted");
}
config.abortSignal = options.signal;
}
if (options.cachedContent !== undefined) {
// Blank names are never valid resource references; anything else stays
// opaque so we do not invent format/model/project checks here.
if (options.cachedContent.trim().length === 0) {
throw new AIError.ValidationError("cachedContent must not be blank");
}
const incompatibleFields = [
config.systemInstruction !== undefined && "systemInstruction",
config.tools !== undefined && "tools",
config.toolConfig !== undefined && "toolConfig",
].filter((field): field is string => Boolean(field));
if (incompatibleFields.length > 0) {
throw new AIError.ValidationError(
`cachedContent cannot be combined with request-level ${incompatibleFields.join(", ")}`,View on GitHub (pinned to 9690622007)
Solutions
- Check signal.aborted before calling and skip the request entirely
- Create a fresh AbortController per request instead of reusing one
- If intentional cancellation, catch AIError.AbortError and treat it as expected control flow
- Adjust request lifecycle so the signal is created at dispatch time, not earlier
Example fix
// before
const controller = new AbortController();
scheduleAbort(controller, timeoutMs); // may abort before send
await streamGoogle(model, params, { signal: controller.signal });
// after
const controller = new AbortController();
scheduleAbort(controller, timeoutMs);
if (controller.signal.aborted) return; // skip cancelled work
await streamGoogle(model, params, { signal: controller.signal }); Defensive patterns
Strategy: validation
Validate before calling
if (signal?.aborted) {
return; // skip the call entirely — no error to handle
} Type guard
function isLive(signal?: AbortSignal): boolean {
return !signal || !signal.aborted;
} Try / catch
try {
await streamGoogle(model, params, { signal });
} catch (err) {
if (err instanceof AIError.AbortError) {
return null; // expected cancellation path
}
throw err;
} Prevention
- Create a fresh AbortController per request; never reuse an already-aborted signal
- Check signal.aborted before dispatching queued work
- Distinguish intentional aborts from failures in your error handling
- Wire request timeouts to fire after dispatch, not before
When it happens
Trigger: Caller passes an AbortSignal that was aborted before streamGoogleGenAI/params was invoked — e.g. a request created after its controlling request/timeout already fired, or a signal aborted synchronously before the call.
Common situations: Race between a request timeout firing and the API call being made; reusing a controller whose abort() already ran; queueing requests and aborting stale ones before they execute.
Related errors
- An unknown error occurred
- Request was aborted
- Auth broker request aborted
- OAuth refresh ownership aborted by caller
- Request was aborted.
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/b56cce015df01d8d.
Report an issue: GitHub.