{"record":{"id":"938fc00c1feec46d","repo":"can1357/oh-my-pi","slug":"aborted-938fc0","errorCode":null,"errorMessage":"Aborted","messagePattern":"Aborted","errorType":"exception","errorClass":"ToolAbortError","httpStatus":null,"severity":"info","filePath":"packages/coding-agent/src/web/scrapers/twitter.ts","lineNumber":74,"sourceCode":"\t\t\t\t\t\tfor (const reply of replies.slice(1, 10)) {\n\t\t\t\t\t\t\tconst replyUser = reply.parentElement?.querySelector(\".username\")?.textContent?.trim();\n\t\t\t\t\t\t\tmd += `**${replyUser || \"@?\"}**: ${reply.textContent?.trim()}\\n\\n`;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\treturn buildResult(md, {\n\t\t\t\t\t\turl,\n\t\t\t\t\t\tfinalUrl: nitterUrl,\n\t\t\t\t\t\tmethod: \"twitter-nitter\",\n\t\t\t\t\t\tfetchedAt,\n\t\t\t\t\t\tnotes: [`Via Nitter: ${instance}`],\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} catch {\n\t\tif (signal?.aborted) {\n\t\t\tthrow new ToolAbortError();\n\t\t}\n\t}\n\n\tif (signal?.aborted) {\n\t\tthrow new ToolAbortError();\n\t}\n\n\t// X.com blocks all bots - return a helpful error instead of falling through\n\treturn {\n\t\turl,\n\t\tfinalUrl: url,\n\t\tcontentType: \"text/plain\",\n\t\tmethod: \"twitter-blocked\",\n\t\tcontent:\n\t\t\t\"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\",\n\t\tfetchedAt: new Date().toISOString(),\n\t\ttruncated: false,\n\t\tnotes: [\"X.com blocks bots; Nitter instances unavailable\"],","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/web/scrapers/twitter.ts#L56-L92","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: treating abort as failure\ntry {\n  await webFetch(tweetUrl, { signal });\n} catch (err) {\n  retryQueue.push(tweetUrl); // requeues even when user cancelled\n}\n// after\ntry {\n  await webFetch(tweetUrl, { signal });\n} catch (err) {\n  if (err instanceof ToolAbortError || signal?.aborted) return; // cancelled — do not retry\n  retryQueue.push(tweetUrl);\n}","handlingStrategy":"try-catch","validationCode":"if (signal?.aborted) return; // don't start a Nitter rotation on an already-cancelled request","typeGuard":"function isAbort(err: unknown): err is ToolAbortError {\n  return err instanceof ToolAbortError;\n}","tryCatchPattern":"try {\n  const res = await webFetch(tweetUrl, { signal });\n} catch (err) {\n  if (err instanceof ToolAbortError || signal?.aborted) return; // user cancelled — not an error\n  throw err;\n}","preventionTips":["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."],"tags":["abort","cancellation","timeout","twitter"],"backgroundTag":"operation-aborted","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}