can1357/oh-my-pi · info · ToolAbortError
Aborted
Error message
Aborted
What it means
loadPage, the shared fetch helper for all web-fetch special handlers, checks `signal?.aborted` at the top of each user-agent retry iteration and throws ToolAbortError immediately if the caller's signal is already cancelled. This is deliberate: a fetch would still be attempted with an aborted combined signal, so the helper short-circuits with the canonical tool-cancellation error.
Source
Thrown at packages/coding-agent/src/web/scrapers/types.ts:145
return new TextDecoder(label as Bun.Encoding).decode(bytes);
} catch {
// Unknown/unsupported label — fall back to UTF-8.
}
}
return bytes.toString("utf-8");
}
/**
* Fetch a page with timeout and size limit
*/
export async function loadPage(url: string, options: LoadPageOptions = {}): Promise<LoadPageResult> {
const { timeout = 20, headers = {}, maxBytes = MAX_BYTES, signal, method = "GET", body } = options;
let lastError: string | undefined;
let retried429 = false;
for (let attempt = 0; attempt < USER_AGENTS.length; attempt++) {
if (signal?.aborted) {
throw new ToolAbortError();
}
const userAgent = USER_AGENTS[attempt];
const requestSignal = ptree.combineSignals(signal, timeout * 1000);
try {
const requestInit: RequestInit = {
signal: requestSignal,
method,
headers: {
"User-Agent": userAgent,
Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "identity", // Cloudflare Markdown-for-Agents returns corrupted bytes when compression is negotiated
...headers,
},
redirect: "follow",
};View on GitHub (pinned to 9690622007)
Solutions
- No fix needed — this is intentional cancellation; catch ToolAbortError in the caller and stop work.
- If it fires without user action, audit the AbortSignal source for premature aborts (short timeouts, leaked controllers aborted elsewhere).
- Increase the timeout option passed to loadPage/web-fetch so long fetches complete before the combined timeout signal fires.
- Avoid reusing an already-aborted AbortController across queued fetches; create a fresh controller per request.
- Check callers for aborting a shared controller on one failed request while other requests still need it.
Example fix
// before: one shared controller for a batch; one abort kills every loadPage call
const controller = new AbortController();
for (const url of urls) await loadPage(url, { signal: controller.signal });
// after: per-request controller with its own timeout
for (const url of urls) {
const controller = new AbortController();
try {
await loadPage(url, { signal: controller.signal, timeout: 20 });
} catch (err) {
if (err instanceof ToolAbortError) break; // only this request's signal fired
throw err;
}
} Defensive patterns
Strategy: try-catch
Validate before calling
if (signal?.aborted) return; // avoid calling loadPage with a pre-aborted signal
Type guard
function isAbort(err: unknown): err is ToolAbortError {
return err instanceof ToolAbortError;
} Try / catch
try {
const page = await loadPage(url, { signal, timeout: 20 });
} catch (err) {
if (err instanceof ToolAbortError) return; // cancellation — stop, don't retry
throw err;
} Prevention
- Check signal.aborted before each queued loadPage call.
- Use a fresh AbortController per request; never share one across a batch.
- Never abort a controller as an error-handling side effect on unrelated requests.
- Set the timeout option deliberately — it feeds the combined timeout signal that triggers aborts.
- Catch ToolAbortError explicitly and treat it as cancellation, never as a retryable failure.
When it happens
Trigger: Any handler (result, response, altResult, readmeResult, postResult, topicResult — e.g. GitHub, Reddit, HN scrapers) calling loadPage after its AbortSignal has fired: user pressed Esc, a timeout elapsed, session shutdown, or a 429 backoff wait was interrupted. Also fires when the signal aborts between user-agent attempts during bot-block retry rotation.
Common situations: User cancels a slow page fetch mid user-agent rotation; combined timeout signal (timeout*1000 via ptree.combineSignals) expires between attempts; Retry-After backoff for a 429 overlaps signal abort; upstream very slow so users cancel routinely.
Related errors
- completion() request aborted.
- ${isTimedOutJuliaCancellation(executionOptions.signal.reason
- Aborted
- Request was aborted
- Request was aborted
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/325f41ca732906db.
Report an issue: GitHub.