can1357/oh-my-pi · warning · ToolAbortError
${job.getLatestText() || "Command aborted"}
Error message
${job.getLatestText() || "Command aborted"} What it means
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.
Source
Thrown at packages/coding-agent/src/tools/bash.ts:1089
// Suppress the completion delivery up front so a job finishing while we
// foreground-wait cannot also be injected by the delivery loop. Lifted
// via resumeDeliveries() if we end up backgrounding after all.
autoBgManager.acknowledgeDeliveries([job.jobId]);
const waitResult = await raceJobSettlement(
job.completion,
autoBackgroundWaitMs,
signal,
ctx?.toolCall?.steeringSignal,
);
if (waitResult.kind === "completed") {
return waitResult.result;
}
if (waitResult.kind === "failed") {
throw waitResult.error;
}
if (waitResult.kind === "aborted") {
autoBgManager.cancel(job.jobId);
throw new ToolAbortError(job.getLatestText() || "Command aborted");
}
job.stopUpdates();
autoBgManager.resumeDeliveries([job.jobId]);
// "steer": a queued user/peer message arrived mid-wait — background
// the command (it keeps running) so the message injects promptly.
const notices =
waitResult.kind === "steer"
? [...pendingNotices, "Backgrounded early to handle an incoming message; the command keeps running."]
: pendingNotices;
return this.#buildBackgroundStartResult(job.jobId, job.getLatestText(), timeoutSec, {
requestedTimeoutSec,
notices,
});
}
// Fold direnv/devenv env into (command, env) ONCE for the two backends
// that bypass `executeBash` — the ACP client terminal and the PTY. The
// `executeBash` branch below is intentionally excluded: it runs its ownView on GitHub (pinned to 9690622007)
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.
Defensive patterns
Strategy: try-catch
Try / catch
try {
await bash.execute(id, { command }, signal);
} catch (e) {
if (e instanceof ToolAbortError) {
// message carries the job's last output — record it for the user
logger.info("background command aborted", { partialOutput: e.message });
return; // intentional cancellation
}
throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: Users pressing escape on a slow running command; the agent framework cancelling a turn; nested timeouts in the host application aborting tool signals.
Related errors
- Request was aborted
- Auth broker request aborted
- OAuth refresh ownership aborted by caller
- Request was aborted.
- AbortError
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/df2b91dfc5f8fd16.
Report an issue: GitHub.