Shell wrapper treats exit code 1 as fatal error. But exit code 1 is used for many legitimate conditions: grep (no matches), diff (files differ), test (condition false). Wrapper should only treat exit code >= 2 as fatal.
Fix
// Before: if (exitCode !== 0) throw Error
// After: if (exitCode >= 2) throw Error // 0-1 = pass through
Shell wrapper treats ALL non-zero exit codes as fatal. But many standard Unix commands use exit code 1 for legitimate non-error conditions: grep (no match), diff (files differ), test/[ (condition false), sort -c (unsorted).
Fix
Exit code classification:
const LEGITIMATE_EXIT_CODES = {
'grep': new Set([0, 1, 2]),
'diff': new Set([0, 1, 2]),
'test': new Set([0, 1]),
'[': new Set([0, 1]),
};
function isLegitimateExitCode(cmd, code) {
const allowed = LEGITIMATE_EXIT_CODES[cmd.split(' ')[0]];
return allowed ? allowed.has(code) : code === 0;
}
I looked at current openai/codex@7d47056. The part that seems most relevant to the grep no-match case is not only the wrapper shape, but the result mapping after the command returns.
codex-rs/core/src/tasks/user_shell.rs and codex-rs/core/src/tools/events.rs both appear to classify exit_code == 0 as the only successful shell/tool result, so a legitimate grep no-match 1 is still routed through the failure path. codex-rs/core/src/exec.rs does not appear to special-case 1; its quick sandbox-denial check is for 2, 126, and 127.
I would avoid treating every exit code 1 as success globally, because many commands use it for real failures. A safer direction would be to keep the raw exit code visible, but separate "command failed" from "task should abort", or add command/context-specific expected-exit handling for known cases like grep no-match, diff differences, and test false.
3 Comments
Proposed Solution: POSIX-Compliant Exit Code Handling
Root Cause
Shell wrapper treats exit code 1 as fatal error. But exit code 1 is used for many legitimate conditions:
grep(no matches),diff(files differ),test(condition false). Wrapper should only treat exit code >= 2 as fatal.Fix
Testing
grep "nonexistent" file— should return exit code 1, not abortdiff file1 file2(different) — should return exit code 1command_not_found— should return exit code 127Full solution:
solutions/codex-24226-shell-wrapper-exit-code-1-abort-fix.mdProposed Solution: Shell Wrapper Exit Code 1 Aborts Tasks
Root Cause
Shell wrapper treats ALL non-zero exit codes as fatal. But many standard Unix commands use exit code 1 for legitimate non-error conditions:
grep(no match),diff(files differ),test/[(condition false),sort -c(unsorted).Fix
Exit code classification:
Full solution: codex-24226-shell-wrapper-exit-code-1-fix.md
I looked at current
openai/codex@7d47056. The part that seems most relevant to thegrepno-match case is not only the wrapper shape, but the result mapping after the command returns.codex-rs/core/src/tasks/user_shell.rsandcodex-rs/core/src/tools/events.rsboth appear to classifyexit_code == 0as the only successful shell/tool result, so a legitimategrepno-match1is still routed through the failure path.codex-rs/core/src/exec.rsdoes not appear to special-case1; its quick sandbox-denial check is for2,126, and127.I would avoid treating every exit code
1as success globally, because many commands use it for real failures. A safer direction would be to keep the raw exit code visible, but separate "command failed" from "task should abort", or add command/context-specific expected-exit handling for known cases likegrepno-match,diffdifferences, andtestfalse.