can1357/oh-my-pi · error · ToolError
${finalText}
Error message
${finalText} What it means
When a backgrounded bash job finishes with isError=true (typically a non-zero exit code), the job's runner re-throws the command's final output text as a ToolError. This re-enters the failure path so the AsyncJobManager records the job as failed and delivers the error text, matching the behavior of foreground execution. The message content is the command's own captured output, not a library diagnostic.
Source
Thrown at packages/coding-agent/src/tools/bash.ts:863
onMinimizedSave: originalText => saveBashOriginalArtifact(this.session, originalText),
});
const wallTimeMs = performance.now() - wallTimeStart;
const finalResult = await this.#buildCompletedResult(result, options.timeoutSec, {
requestedTimeoutSec: options.requestedTimeoutSec,
notices: options.notices ?? [],
wallTimeMs,
});
const finalText = this.#extractTextResult(finalResult);
latestText = finalText;
// Hand the detailed result to the foreground auto-background
// waiter (which renders it, footer included) before deciding
// the job's terminal state.
completion.resolve({ kind: "completed", result: finalResult });
if (finalResult.isError === true) {
// A non-zero exit is a completed command that failed. Re-enter
// the failure path so the job manager records it as failed and
// delivers the error text, matching prior throw-based behavior.
throw new ToolError(finalText);
}
await reportProgress(finalText, { async: { state: "completed", jobId, type: "bash" } });
return finalText;
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
latestText = message;
completion.resolve({ kind: "failed", error });
await reportProgress(message, { async: { state: "failed", jobId, type: "bash" } });
throw error;
}
},
{
ownerId: this.session.getAgentId?.() ?? undefined,
onProgress: async text => {
latestText = text;
if (!forwardUpdates) return;
await options.onUpdate?.({
content: [{ type: "text", text }],View on GitHub (pinned to 9690622007)
Solutions
- Read the error text: it is the command's real output; fix whatever the command reported.
- If the exit code is meaningful-but-not-failure (grep, diff, test harnesses), append `|| true` or inspect exit codes explicitly in the script.
- Check the job's output artifact for full logs when the tail text is truncated.
Example fix
// before: background command fails on grep no-match
await bash.execute(id, { command: "grep TODO src/", async: true });
// after: treat no-match as success
await bash.execute(id, { command: "grep TODO src/ || true", async: true }); Defensive patterns
Strategy: try-catch
Try / catch
try {
await bash.execute(id, { command, async: true });
} catch (e) {
if (e instanceof ToolError) {
// message is the command's own output; log/inspect it as command failure
logger.warn("background command failed", { output: e.message });
} else throw e;
} Prevention
- Design background commands to exit 0 (append `|| true` for signal-style exit codes).
- Capture the job's output artifact for full logs, not just the tail.
- Test background commands in the foreground first to confirm exit behavior.
When it happens
Trigger: Running any command in async/auto-background mode whose process exits with a non-zero status (e.g. `npm test` failing, `grep` finding no match, a build error).
Common situations: Failing test suites or builds left running in the background; commands like `diff`/`grep` that use exit codes as signals; scripts whose last statement returns non-zero.
Related errors
- Command exited with code ${result.exitCode}
- ${outputText}\n\nCommand failed: missing exit status
- %%bash requires a POSIX bash, but none was found. Install Gi
- Command aborted
- Command timed out after ${err.message.slice("timeout:".lengt
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/de9c9c457131c745.
Report an issue: GitHub.