earendil-works/pi · warning · Error

Command aborted

Error message

Command aborted

What it means

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.

Source

Thrown at packages/agent/src/harness/tools/bash.ts:145

				let outputText = capture.output;
				let details: BashToolDetails | undefined;
				if (capture.truncation.truncated) {
					details = { truncation: capture.truncation, fullOutputPath: capture.fullOutputPath };
					const startLine = capture.truncation.totalLines - capture.truncation.outputLines + 1;
					const endLine = capture.truncation.totalLines;
					if (capture.truncation.lastLinePartial) {
						const lastLineSize = formatSize(capture.lastLineBytes);
						outputText += `\n\n[Showing last ${formatSize(capture.truncation.outputBytes)} of line ${endLine} (line is ${lastLineSize}). Full output: ${capture.fullOutputPath}]`;
					} else if (capture.truncation.truncatedBy === "lines") {
						outputText += `\n\n[Showing lines ${startLine}-${endLine} of ${capture.truncation.totalLines}. Full output: ${capture.fullOutputPath}]`;
					} else {
						outputText += `\n\n[Showing lines ${startLine}-${endLine} of ${capture.truncation.totalLines} (${formatSize(DEFAULT_MAX_BYTES)} limit). Full output: ${capture.fullOutputPath}]`;
					}
				}

				const appendStatus = (status: string): string => `${outputText ? `${outputText}\n\n` : ""}${status}`;
				if (capture.cancelled) throw new Error(appendStatus("Command aborted"));
				if (capture.executionError?.code === "timeout") {
					throw new Error(appendStatus(`Command timed out after ${timeout} seconds`), {
						cause: capture.executionError,
					});
				}
				if (capture.executionError) throw capture.executionError;
				if (capture.exitCode !== 0 && capture.exitCode !== undefined) {
					throw new Error(appendStatus(`Command exited with code ${capture.exitCode}`));
				}
				return { content: [{ type: "text", text: outputText || "(no output)" }], details };
			} finally {
				clearUpdateTimer();
			}
		},
	};
}

View on GitHub (pinned to 4af9d21d3b)

Solutions

  1. Treat it as a clean cancellation: check your controller's state instead of retrying the command
  2. Use the timeout field for time limits instead of aborting the signal
  3. If partial output matters, parse the captured text prefixed before 'Command aborted'

Example fix

// before
controller.abort();
await result; // unhandled 'Command aborted'

// after
controller.abort();
await result.catch((e) => {
  if (controller.signal.aborted) return; // expected cancellation
  throw e;
});
Defensive patterns

Strategy: try-catch

Type guard

function isAbortError(e: unknown, signal: AbortSignal | undefined): e is Error {
  return e instanceof Error && e.message.endsWith('Command aborted') && signal?.aborted === true;
}

Try / catch

try {
  return await bashTool.execute(toolCallId, { command }, signal);
} catch (e) {
  if (isAbortError(e, signal)) return { content: [], cancelled: true }; // expected cancellation
  throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of earendil-works/pi@4af9d21d3b (2026-08-24). Data as JSON: /api/errors/5b97df777fa16493. Report an issue: GitHub.