{"record":{"id":"df2b91dfc5f8fd16","repo":"can1357/oh-my-pi","slug":"job-getlatesttext-command-aborted","errorCode":null,"errorMessage":"${job.getLatestText() || \"Command aborted\"}","messagePattern":"\\$\\{job\\.getLatestText\\(\\) \\|\\| \"Command aborted\"\\}","errorType":"exception","errorClass":"ToolAbortError","httpStatus":null,"severity":"warning","filePath":"packages/coding-agent/src/tools/bash.ts","lineNumber":1089,"sourceCode":"\t\t\t// Suppress the completion delivery up front so a job finishing while we\n\t\t\t// foreground-wait cannot also be injected by the delivery loop. Lifted\n\t\t\t// via resumeDeliveries() if we end up backgrounding after all.\n\t\t\tautoBgManager.acknowledgeDeliveries([job.jobId]);\n\t\t\tconst waitResult = await raceJobSettlement(\n\t\t\t\tjob.completion,\n\t\t\t\tautoBackgroundWaitMs,\n\t\t\t\tsignal,\n\t\t\t\tctx?.toolCall?.steeringSignal,\n\t\t\t);\n\t\t\tif (waitResult.kind === \"completed\") {\n\t\t\t\treturn waitResult.result;\n\t\t\t}\n\t\t\tif (waitResult.kind === \"failed\") {\n\t\t\t\tthrow waitResult.error;\n\t\t\t}\n\t\t\tif (waitResult.kind === \"aborted\") {\n\t\t\t\tautoBgManager.cancel(job.jobId);\n\t\t\t\tthrow new ToolAbortError(job.getLatestText() || \"Command aborted\");\n\t\t\t}\n\t\t\tjob.stopUpdates();\n\t\t\tautoBgManager.resumeDeliveries([job.jobId]);\n\t\t\t// \"steer\": a queued user/peer message arrived mid-wait — background\n\t\t\t// the command (it keeps running) so the message injects promptly.\n\t\t\tconst notices =\n\t\t\t\twaitResult.kind === \"steer\"\n\t\t\t\t\t? [...pendingNotices, \"Backgrounded early to handle an incoming message; the command keeps running.\"]\n\t\t\t\t\t: pendingNotices;\n\t\t\treturn this.#buildBackgroundStartResult(job.jobId, job.getLatestText(), timeoutSec, {\n\t\t\t\trequestedTimeoutSec,\n\t\t\t\tnotices,\n\t\t\t});\n\t\t}\n\n\t\t// Fold direnv/devenv env into (command, env) ONCE for the two backends\n\t\t// that bypass `executeBash` — the ACP client terminal and the PTY. The\n\t\t// `executeBash` branch below is intentionally excluded: it runs its own","sourceCodeStart":1071,"sourceCodeEnd":1107,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/bash.ts#L1071-L1107","documentation":"In auto-background mode the tool foreground-waits on the job for a threshold window; if the caller's AbortSignal fires during that wait, the tool cancels the job and throws a ToolAbortError whose message is the job's most recent output text (falling back to 'Command aborted'). This surfaces what the command had produced when cancellation arrived, instead of a bare cancellation string.","triggerScenarios":"The user/agent aborts the tool call (esc, session cancel, upstream timeout) while an auto-backgrounded command is still within its foreground wait window; the abort is delivered through the wait race rather than the child process's own signal handling.","commonSituations":"Users pressing escape on a slow running command; the agent framework cancelling a turn; nested timeouts in the host application aborting tool signals.","solutions":["None needed if intentional — the message contains the command's last output; inspect it to see how far the command got.","If aborts are accidental, raise the auto-background threshold or the command timeout so the wait isn't cut short.","To keep the command running past an abort, run it with async=true so it is a managed background job not tied to the call's signal."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await bash.execute(id, { command }, signal);\n} catch (e) {\n  if (e instanceof ToolAbortError) {\n    // message carries the job's last output — record it for the user\n    logger.info(\"background command aborted\", { partialOutput: e.message });\n    return; // intentional cancellation\n  }\n  throw e;\n}","preventionTips":["Pass a real AbortSignal and treat ToolAbortError as expected control flow, not a bug.","Raise autoBackgroundThresholdMs if foreground waits are cancelled too often.","Use async=true for commands that must survive call-level cancellation."],"tags":["abort","cancellation","background-jobs"],"backgroundTag":"tool-call-aborted","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}