can1357/oh-my-pi · warning · ToolError

\n\n[ ]

Error message

${out}\n\n[${message}]

What it means

When result.timedOut === true, BashTool throws a ToolError reporting the timeout. The message is 'Command timed out' or 'Command timed out after N seconds' (when the tool call specified a timeoutSec), and any captured output is prepended so the user sees what the command produced before it was killed.

Solutions

  1. Increase the timeoutSec parameter for legitimately long commands.
  2. Run long-lived processes in the background (nohup ... &, redirect logs) and poll them instead of blocking the tool call.
  3. Fix the command so it actually terminates — add its own timeouts, remove interactive prompts, pipe input.
  4. Disable pagers/interactive behavior with flags (e.g. --no-progress, CI=1, </dev/null) that commonly cause hangs.

Example fix

// before
await bash.run("npm install", {}); // hangs past default timeout
// after
await bash.run("npm install", { timeoutSec: 600 });
Defensive patterns

Strategy: try-catch

Try / catch

try { await bash.run(cmd, opts); } catch (e) { if (e instanceof ToolError && e.message.includes('Command timed out')) { /* raise timeoutSec, background the process, or fix the hang */ } else throw e; }

Prevention

When it happens

Trigger: The bash tool call exceeded its allowed runtime: a timeoutSec was supplied in the tool arguments and the command ran longer than it, or a default timeout fired (timeoutSec undefined).

Common situations: Long-running installs, builds, watches, servers, or interactive prompts (commands waiting on stdin) that never exit; network operations hanging without timeouts of their own.

Related errors


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

Appendix: source

Thrown at packages/coding-agent/src/tools/bash.ts:664

	 */
	#throwIfUnfinished(
		result: BashResult | BashInteractiveResult,
		timeoutSec: number | undefined,
		outputText: string,
	): void {
		if (result.cancelled) {
			// Local executor output already carries a leading `[Command cancelled]`
			// notice from the sink; PTY/bridge output does not, so annotate only
			// the latter.
			const out = normalizeResultOutput(result);
			const annotated = out.startsWith("[Command cancelled]") ? out : out ? `${out}\n\n[Command aborted]` : out;
			throw new ToolError(annotated || "Command aborted");
		}
		if (result.timedOut === true) {
			const out = normalizeResultOutput(result);
			const message =
				timeoutSec === undefined ? "Command timed out" : `Command timed out after ${timeoutSec} seconds`;
			throw new ToolError(out ? `${out}\n\n[${message}]` : message);
		}
		if (result.exitCode === undefined) {
			throw new ToolError(`${outputText}\n\nCommand failed: missing exit status`);
		}
	}

	async #buildCompletedResult(
		result: BashResult | BashInteractiveResult,
		timeoutSec: number | undefined,
		options: {
			requestedTimeoutSec?: number;
			notices?: readonly string[];
			terminalId?: string;
			wallTimeMs?: number;
		} = {},
	): Promise<AgentToolResult<BashToolDetails>> {
		const exitCode = result.exitCode;
		const failedExit = exitCode !== undefined && exitCode !== 0;

View on GitHub (pinned to 9690622007)