{"record":{"id":"5b97df777fa16493","repo":"earendil-works/pi","slug":"command-aborted","errorCode":null,"errorMessage":"Command aborted","messagePattern":"Command aborted","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"packages/agent/src/harness/tools/bash.ts","lineNumber":145,"sourceCode":"\n\t\t\t\tlet outputText = capture.output;\n\t\t\t\tlet details: BashToolDetails | undefined;\n\t\t\t\tif (capture.truncation.truncated) {\n\t\t\t\t\tdetails = { truncation: capture.truncation, fullOutputPath: capture.fullOutputPath };\n\t\t\t\t\tconst startLine = capture.truncation.totalLines - capture.truncation.outputLines + 1;\n\t\t\t\t\tconst endLine = capture.truncation.totalLines;\n\t\t\t\t\tif (capture.truncation.lastLinePartial) {\n\t\t\t\t\t\tconst lastLineSize = formatSize(capture.lastLineBytes);\n\t\t\t\t\t\toutputText += `\\n\\n[Showing last ${formatSize(capture.truncation.outputBytes)} of line ${endLine} (line is ${lastLineSize}). Full output: ${capture.fullOutputPath}]`;\n\t\t\t\t\t} else if (capture.truncation.truncatedBy === \"lines\") {\n\t\t\t\t\t\toutputText += `\\n\\n[Showing lines ${startLine}-${endLine} of ${capture.truncation.totalLines}. Full output: ${capture.fullOutputPath}]`;\n\t\t\t\t\t} else {\n\t\t\t\t\t\toutputText += `\\n\\n[Showing lines ${startLine}-${endLine} of ${capture.truncation.totalLines} (${formatSize(DEFAULT_MAX_BYTES)} limit). Full output: ${capture.fullOutputPath}]`;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst appendStatus = (status: string): string => `${outputText ? `${outputText}\\n\\n` : \"\"}${status}`;\n\t\t\t\tif (capture.cancelled) throw new Error(appendStatus(\"Command aborted\"));\n\t\t\t\tif (capture.executionError?.code === \"timeout\") {\n\t\t\t\t\tthrow new Error(appendStatus(`Command timed out after ${timeout} seconds`), {\n\t\t\t\t\t\tcause: capture.executionError,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tif (capture.executionError) throw capture.executionError;\n\t\t\t\tif (capture.exitCode !== 0 && capture.exitCode !== undefined) {\n\t\t\t\t\tthrow new Error(appendStatus(`Command exited with code ${capture.exitCode}`));\n\t\t\t\t}\n\t\t\t\treturn { content: [{ type: \"text\", text: outputText || \"(no output)\" }], details };\n\t\t\t} finally {\n\t\t\t\tclearUpdateTimer();\n\t\t\t}\n\t\t},\n\t};\n}\n","sourceCodeStart":127,"sourceCodeEnd":162,"githubUrl":"https://github.com/earendil-works/pi/blob/4af9d21d3b4d664e4a29fcabfec85171077248e3/packages/agent/src/harness/tools/bash.ts#L127-L162","documentation":"After the shell capture finishes, bash execute checks capture.cancelled and converts it into this Error, prefixed with whatever output was captured so far (bash.ts:145). cancelled means the AbortSignal passed to execute was aborted, that is, the caller cancelled the tool call. It is the expected outcome of cancellation, not a command failure; exit code and executionError are handled by separate branches below it.","triggerScenarios":"The harness aborts the run's signal because the user hit stop or interrupt; a request-level timeout aborts a shared AbortController; test cleanup aborts a controller still in use by a running command.","commonSituations":"User interrupts an agent run mid-command; one AbortController reused across tool calls so an early cancellation kills later commands; race logic that aborts a controller after losing.","solutions":["Treat it as a clean cancellation: check your controller's state instead of retrying the command","Use the timeout field for time limits instead of aborting the signal","If partial output matters, parse the captured text prefixed before 'Command aborted'"],"exampleFix":"// before\ncontroller.abort();\nawait result; // unhandled 'Command aborted'\n\n// after\ncontroller.abort();\nawait result.catch((e) => {\n  if (controller.signal.aborted) return; // expected cancellation\n  throw e;\n});","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isAbortError(e: unknown, signal: AbortSignal | undefined): e is Error {\n  return e instanceof Error && e.message.endsWith('Command aborted') && signal?.aborted === true;\n}","tryCatchPattern":"try {\n  return await bashTool.execute(toolCallId, { command }, signal);\n} catch (e) {\n  if (isAbortError(e, signal)) return { content: [], cancelled: true }; // expected cancellation\n  throw e;\n}","preventionTips":["Check controller.signal.aborted before treating a bash failure as a command error","Use the timeout field for time limits; reserve signal abort for cancellation","Don't share one AbortController across sequential tool calls"],"tags":["bash","abort","cancellation","tool-execution"],"backgroundTag":"operation-aborted","analyzedSha":"4af9d21d3b4d664e4a29fcabfec85171077248e3","analyzedAt":"2026-08-24T13:07:14.692Z","schemaVersion":2},"datasetVersion":"2026-08-24T17:17:21.512Z"}