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

  1. No fix needed — this is intentional cancellation signaling; let it propagate and report the operation as cancelled.
  2. If it surfaces unexpectedly, check whether the AbortSignal is being aborted prematurely (short timeout, early Esc).
  3. If Nitter is consistently slow enough that users cancel, raise the tool timeout.
  4. 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

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


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