{"record":{"id":"a3b2cd886e8a20ba","repo":"can1357/oh-my-pi","slug":"toolaborterror-operation-aborted-a3b2cd","errorCode":null,"errorMessage":"ToolAbortError (operation aborted)","messagePattern":"ToolAbortError \\(operation aborted\\)","errorType":"exception","errorClass":"ToolAbortError","httpStatus":null,"severity":"info","filePath":"packages/coding-agent/src/tools/read-pdf.ts","lineNumber":131,"sourceCode":"\t\t\t\tsignal: renderSignal,\n\t\t\t\townerSessionId: session.getSessionId?.() ?? undefined,\n\t\t\t}),\n\t\t);\n\t\ttabOpened = true;\n\t\tawait releaseBrowser(acquiredBrowser, { kill: false });\n\t\tbrowserLease = false;\n\n\t\tconst result = await runInTab(tabName, {\n\t\t\tcode: PDF_SCREENSHOT_CODE,\n\t\t\ttimeoutMs: PDF_RENDER_TIMEOUT_MS,\n\t\t\tsignal: renderSignal,\n\t\t\tsession,\n\t\t});\n\t\tconst screenshot = result.screenshots.at(-1);\n\t\tif (!screenshot) throw new ToolError(`Chromium did not capture PDF page ${page}.`);\n\t\treturn screenshot;\n\t} catch (error) {\n\t\tif (signal?.aborted) throw new ToolAbortError();\n\t\tif (timeoutSignal.aborted) {\n\t\t\tthrow new ToolError(`Timed out rendering PDF page ${page} in Chromium.`);\n\t\t}\n\t\tthrow error;\n\t} finally {\n\t\tif (tabOpened) await releaseTab(tabName, { kill: false });\n\t\tif (browserLease && browser) await releaseBrowser(browser, { kill: false });\n\t}\n}\n","sourceCodeStart":113,"sourceCodeEnd":141,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/read-pdf.ts#L113-L141","documentation":"renderPdfPageScreenshot renders a single PDF page to a screenshot by loading it in a headless Chromium tab. When the caller-supplied AbortSignal (the tool call's cancellation signal) fires before rendering completes, the catch block rethrows as ToolAbortError ('operation aborted') instead of the underlying error. This signals orderly cancellation, not a failure of the renderer itself.","triggerScenarios":"The user cancels the tool call (Esc / abort) while Chromium is acquiring the browser, opening the PDF tab, or running PDF_SCREENSHOT_CODE in the tab; renderSignal = AbortSignal.any([signal, timeoutSignal]) propagates the abort and the catch checks signal?.aborted first.","commonSituations":"User interrupts a slow PDF read; an orchestration layer cancels the agent turn mid-render; a long browser-acquisition queue (cold Chromium startup) makes the user give up before the render finishes.","solutions":["No fix needed if the abort was intentional — treat ToolAbortError as normal cancellation and stop retrying.","If aborts happen unintentionally, check upstream wiring that shares one AbortSignal across many tool calls.","Reduce render latency by reusing a warm browser so renders complete before the user cancels.","Check that the cancellation UI doesn't fire spuriously (e.g. short client timeouts)."],"exampleFix":"// before: treating abort as a crash\ntry { shot = await renderPdfPageScreenshot(...) } catch (e) { log(e); }\n// after\ntry { shot = await renderPdfPageScreenshot(...) }\ncatch (e) { if (e instanceof ToolAbortError) return; /* user cancelled */ throw e; }","handlingStrategy":"try-catch","validationCode":"// before calling\nif (signal?.aborted) return; // don't start work already cancelled","typeGuard":"function isToolAbortError(e: unknown): boolean {\n  return e instanceof Error && e.name === 'ToolAbortError';\n}","tryCatchPattern":"try {\n  const shot = await renderPdfPageScreenshot(session, pdfPath, page, signal);\n} catch (e) {\n  if (isToolAbortError(e)) return; // expected cancellation, not a failure\n  throw e;\n}","preventionTips":["Pass the caller's AbortSignal through so cancellation is recognized, not masked.","Treat ToolAbortError as control flow: swallow or propagate silently, never retry.","Keep renders fast (warm browser) so users rarely need to cancel mid-render.","Never wrap aborts in generic retry loops — retrying an aborted call loops forever."],"tags":["abort","cancellation","pdf","chromium"],"backgroundTag":"operation-aborted","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}