mirror of
https://github.com/moltbot/moltbot.git
synced 2026-03-08 06:54:24 +00:00
fix(exec): treat shell exit codes 126/127 as failures instead of completed
When a command exits with code 127 (command not found) or 126 (not executable), the exec tool previously returned status "completed" with the error buried in the output text. This caused cron jobs to report status "ok" and never increment consecutiveErrors, silently swallowing failures like `python: command not found` across multiple daily cycles. Now these shell-reserved exit codes are classified as "failed", which propagates through the cron pipeline to properly increment consecutiveErrors and surface the issue for operator attention. Fixes #24587 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -482,7 +482,13 @@ export async function runExecProcess(opts: {
|
||||
.then((exit): ExecProcessOutcome => {
|
||||
const durationMs = Date.now() - startedAt;
|
||||
const isNormalExit = exit.reason === "exit";
|
||||
const status: "completed" | "failed" = isNormalExit ? "completed" : "failed";
|
||||
const exitCode = exit.exitCode ?? 0;
|
||||
// Shell exit codes 126 (not executable) and 127 (command not found) are
|
||||
// unrecoverable infrastructure failures that should surface as real errors
|
||||
// rather than silently completing — e.g. `python: command not found`.
|
||||
const isShellFailure = exitCode === 126 || exitCode === 127;
|
||||
const status: "completed" | "failed" =
|
||||
isNormalExit && !isShellFailure ? "completed" : "failed";
|
||||
|
||||
markExited(session, exit.exitCode, exit.exitSignal, status);
|
||||
maybeNotifyOnExit(session, status);
|
||||
@@ -491,7 +497,6 @@ export async function runExecProcess(opts: {
|
||||
}
|
||||
const aggregated = session.aggregated.trim();
|
||||
if (status === "completed") {
|
||||
const exitCode = exit.exitCode ?? 0;
|
||||
const exitMsg = exitCode !== 0 ? `\n\n(Command exited with code ${exitCode})` : "";
|
||||
return {
|
||||
status: "completed",
|
||||
@@ -502,8 +507,11 @@ export async function runExecProcess(opts: {
|
||||
timedOut: false,
|
||||
};
|
||||
}
|
||||
const reason =
|
||||
exit.reason === "overall-timeout"
|
||||
const reason = isShellFailure
|
||||
? exitCode === 127
|
||||
? "Command not found"
|
||||
: "Command not executable (permission denied)"
|
||||
: exit.reason === "overall-timeout"
|
||||
? `Command timed out after ${opts.timeoutSec} seconds`
|
||||
: exit.reason === "no-output-timeout"
|
||||
? "Command timed out waiting for output"
|
||||
|
||||
Reference in New Issue
Block a user