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 own

View on GitHub (pinned to 9690622007)

Solutions

  1. None needed if intentional — the message contains the command's last output; inspect it to see how far the command got.
  2. If aborts are accidental, raise the auto-background threshold or the command timeout so the wait isn't cut short.
  3. 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

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


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/df2b91dfc5f8fd16. Report an issue: GitHub.