can1357/oh-my-pi · info · ToolAbortError
Aborted
Error message
Aborted
What it means
handleTwitter wraps its entire Nitter-scraping flow in a try/catch; when any loadPage or parsing call throws AND the caller's AbortSignal is already aborted, the handler rethrows a ToolAbortError instead of swallowing the failure. This is the tool- cancellation contract: the 'Aborted' error signals the user (or a timeout) cancelled the fetch, not that scraping failed.
Source
Thrown at packages/coding-agent/src/web/scrapers/twitter.ts:74
for (const reply of replies.slice(1, 10)) {
const replyUser = reply.parentElement?.querySelector(".username")?.textContent?.trim();
md += `**${replyUser || "@?"}**: ${reply.textContent?.trim()}\n\n`;
}
}
return buildResult(md, {
url,
finalUrl: nitterUrl,
method: "twitter-nitter",
fetchedAt,
notes: [`Via Nitter: ${instance}`],
});
}
}
}
} catch {
if (signal?.aborted) {
throw new ToolAbortError();
}
}
if (signal?.aborted) {
throw new ToolAbortError();
}
// X.com blocks all bots - return a helpful error instead of falling through
return {
url,
finalUrl: url,
contentType: "text/plain",
method: "twitter-blocked",
content:
"Twitter/X blocks automated access. Nitter instances were unavailable.\n\nTry:\n- Opening the link in a browser\n- Using a different Nitter instance manually\n- Checking if the tweet is available via an archive service",
fetchedAt: new Date().toISOString(),
truncated: false,
notes: ["X.com blocks bots; Nitter instances unavailable"],View on GitHub (pinned to 9690622007)
Solutions
- No fix needed — this is intentional cancellation signaling; let it propagate and report the operation as cancelled.
- If it surfaces unexpectedly, check whether the AbortSignal is being aborted prematurely (short timeout, early Esc).
- If Nitter is consistently slow enough that users cancel, raise the tool timeout.
- Verify the signal passed to the tool call isn't tied to an over-eager parent timeout.
Example fix
// before: treating abort as failure
try {
await webFetch(tweetUrl, { signal });
} catch (err) {
retryQueue.push(tweetUrl); // requeues even when user cancelled
}
// after
try {
await webFetch(tweetUrl, { signal });
} catch (err) {
if (err instanceof ToolAbortError || signal?.aborted) return; // cancelled — do not retry
retryQueue.push(tweetUrl);
} Defensive patterns
Strategy: try-catch
Validate before calling
if (signal?.aborted) return; // don't start a Nitter rotation on an already-cancelled request
Type guard
function isAbort(err: unknown): err is ToolAbortError {
return err instanceof ToolAbortError;
} Try / catch
try {
const res = await webFetch(tweetUrl, { signal });
} catch (err) {
if (err instanceof ToolAbortError || signal?.aborted) return; // user cancelled — not an error
throw err;
} Prevention
- Always distinguish ToolAbortError from real failures; never retry aborted work.
- Check signal.aborted before starting long Nitter rotations.
- Keep per-attempt timeouts short (the handler caps at 10s) so cancels resolve quickly.
- Pass the original caller signal through to loadPage so cancellation propagates.
- If users cancel often, the Nitter instances are probably too slow — rotate in faster ones.
When it happens
Trigger: Calling web-fetch on a twitter.com/x.com URL while the abort signal fires (user pressed Esc, request timeout, session shutdown) during a Nitter loadPage attempt; the thrown error (typically AbortError from fetch or timeout) is caught by the bare `catch` and converted to ToolAbortError because signal.aborted is true.
Common situations: User cancels a slow tweet fetch while all four Nitter instances are timing out (each capped at 10s, so a cancel mid-sequence is common); parent operation times out; tool shutdown while awaiting Nitter responses.
Related errors
- completion() request aborted.
- ${isTimedOutJuliaCancellation(executionOptions.signal.reason
- Aborted
- Request was aborted
- Auth broker request aborted
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/938fc00c1feec46d.
Report an issue: GitHub.