{"record":{"id":"c139746768022802","repo":"can1357/oh-my-pi","slug":"request-was-aborted","errorCode":null,"errorMessage":"Request was aborted","messagePattern":"Request was aborted","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"packages/agent/src/agent.ts","lineNumber":1598,"sourceCode":"\t\t\t\t}\n\n\t\t\t\t// Emit to listeners\n\t\t\t\tthis.#emit(event);\n\t\t\t}\n\n\t\t\t// Handle any remaining partial message\n\t\t\tif (partial && partial.role === \"assistant\" && Array.isArray(partial.content) && partial.content.length > 0) {\n\t\t\t\tconst onlyEmpty = !partial.content.some(\n\t\t\t\t\tc =>\n\t\t\t\t\t\t(c.type === \"thinking\" && c.thinking.trim().length > 0) ||\n\t\t\t\t\t\t(c.type === \"text\" && c.text.trim().length > 0) ||\n\t\t\t\t\t\t(c.type === \"toolCall\" && c.name.trim().length > 0),\n\t\t\t\t);\n\t\t\t\tif (!onlyEmpty) {\n\t\t\t\t\tthis.appendMessage(partial);\n\t\t\t\t} else {\n\t\t\t\t\tif (loopSignal.aborted) {\n\t\t\t\t\t\tthrow new Error(\"Request was aborted\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tconst stoppedForAbort = loopSignal.aborted;\n\t\t\tconst errorMessage = stoppedForAbort\n\t\t\t\t? abortReasonText(loopSignal)\n\t\t\t\t: err instanceof Error\n\t\t\t\t\t? err.message\n\t\t\t\t\t: String(err);\n\t\t\tconst shouldEmitVisibleError = !stoppedForAbort;\n\t\t\tconst assistantPartial = partial?.role === \"assistant\" ? partial : undefined;\n\t\t\tconst hadAssistantStart = assistantPartial !== undefined;\n\t\t\t// Same contract as the normal drain in `#emitCursorSplitAssistantMessage`:\n\t\t\t// a transformer still in flight must be awaited before the payload is\n\t\t\t// snapshotted, or its rewrite patches an entry this catch path already\n\t\t\t// detached and the original is persisted instead. A provider error is\n\t\t\t// exactly when a transform is most likely to be mid-flight.","sourceCodeStart":1580,"sourceCodeEnd":1616,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/agent/src/agent.ts#L1580-L1616","documentation":"The agent loop throws this when a streaming response ends (or is aborted) and the only accumulated content is empty tool calls while the loop's abort signal has fired. It converts an abort that would otherwise be silently swallowed into an explicit error so the loop's catch block can record the run as aborted. It only fires on the path where all partial items were empty tool calls and loopSignal.aborted is true.","triggerScenarios":"Calling agent.prompt()/run() and aborting the loop via an AbortSignal/stop() while the model was streaming a response consisting solely of empty tool-call items; the stream ends with no usable content and loopSignal.aborted is true.","commonSituations":"User hits Ctrl+C / stop button mid-stream right as the model emits tool calls; a timeout cancels the request during a tool-call-only turn; programmatic cancellation races with the stream finishing.","solutions":["Check the AbortSignal you passed (options.signal / loop signal) — if you aborted, handle this error as expected control flow, not a bug","Catch the error and treat loopSignal.aborted (or err message 'Request was aborted') as a graceful-stop path rather than retrying","If you did NOT abort, verify no shared AbortController/timeout is cancelling the request early (e.g. HTTP client timeouts, parent signal propagation)","Retry the prompt with a fresh call if the abort was unintentional and the underlying cause (timeout, shutdown) is resolved"],"exampleFix":"// before\nawait agent.prompt(\"do the thing\", { signal: controller.signal }); // throws on Ctrl+C\n// after\ntry {\n  await agent.prompt(\"do the thing\", { signal: controller.signal });\n} catch (err) {\n  if (controller.signal.aborted) return; // graceful stop\n  throw err;\n}","handlingStrategy":"try-catch","validationCode":"// before prompting\nif (signal?.aborted) {\n  throw new DOMException(\"Aborted before start\", \"AbortError\");\n}","typeGuard":"function isAbortError(err: unknown): boolean {\n  return err instanceof Error && /abort/i.test(err.message);\n}","tryCatchPattern":"try {\n  await agent.prompt(text, { signal });\n} catch (err) {\n  if (signal.aborted || isAbortError(err)) {\n    return; // graceful stop\n  }\n  throw err;\n}","preventionTips":["Always pass a dedicated AbortController per run and inspect signal.aborted in catch blocks","Treat 'Request was aborted' as control flow, not a failure to retry","Audit timeout chains so client timeouts don't fire mid tool-call-only turns"],"tags":["abort","cancellation","streaming","agent-loop"],"backgroundTag":"request-aborted","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}