codex exec exits 0 even when command_execution fails with nonzero exit code
codex exec exits 0 even when the requested command fails and the JSON transcript marks it failed
Summary
When codex exec is used non-interactively to run a prompt that executes a failing shell command, the command failure is correctly recorded inside the transcript, but the outer codex exec process still exits with code 0.
This makes it hard to use codex exec safely in automation, because a failing tool command is not reflected in the CLI process exit status.
Repro
Plain output mode:
codex exec --ephemeral --skip-git-repo-check --color never -C /home/cryptotrading74 "Run exactly: false and stop."
printf "outer_exit=%s
" "$?"
Observed output excerpt:
exec
/bin/bash -lc false in /home/cryptotrading74 exited 1 in 0ms:
codex
Ran `false`. It exited with status `1`.
outer_exit=0
JSON mode:
codex exec --ephemeral --skip-git-repo-check --color never --json -C /home/cryptotrading74 "Run exactly: false and stop." > /tmp/codex_false_exit.jsonl
printf "outer_exit=%s
" "$?"
Observed transcript excerpt:
{"type":"item.completed","item":{"id":"item_1","type":"command_execution","command":"/bin/bash -lc false","exit_code":1,"status":"failed"}}
Observed shell exit:
outer_exit=0
Expected behavior
If the requested command fails and the task ends in that failed state, codex exec should return a nonzero exit code, or provide a documented flag that enables failure-propagating exit behavior.
Actual behavior
The nested command fails, the transcript marks it as failed, but codex exec itself returns 0.
Why this matters
This makes CI and scripting brittle because callers cannot rely on the process exit status to detect command failure.
For comparison, true CLI parse errors do propagate a nonzero exit code correctly. For example:
printf '' | codex exec --ephemeral --skip-git-repo-check --color never -C /home/cryptotrading74 - false
printf "outer_exit=%s\n" "$?"
returns:
error: unexpected argument 'false' found
...
outer_exit=2
So the CLI already uses nonzero exits for real invocation failures, but a nested command_execution failure inside exec still yields outer exit 0.
Files / artifacts
/tmp/codex_false_exit.jsonl/tmp/codex_false_exit_repro2.jsonl
Suggested title
codex exec exits 0 even when command_execution fails with nonzero exit code
2 Comments
This is a significant issue for anyone building CI pipelines around codex exec. Silent success on actual failure is one of the hardest failure modes to debug, because everything downstream assumes the step passed.
We see the same class of problem — "all gates pass, but something is actually broken" — show up frequently in AI-assisted development pipelines. A few patterns we have observed:
The common thread is that the standard success signal (exit code, compilation, test pass) does not reflect the actual state of the code. Each of these requires a different verification strategy — exit code propagation for orchestrators, registry validation for imports, behavioral diffing for test coverage gaps.
We have been building deterministic checks for cases 2 and 3 as a CI gate:
npx @opencodereview/cli scan . --sla L1— catches phantom imports and deprecated API usage that pass all other gates.Repro
main.codex execwas exiting0even when the nested shell command finished with a non-zero status.Root Cause
run_exec_sessiononly flippederror_seenon abort or server-error paths, but did not mark the rollout as failed when it receivedExecCommandEndfor the active task withFailedorDeclinedstatus.exec/src/lib.rsthat markserror_seenon matchingExecCommandEndfailure or decline and added a regression test infailed_command_exit.rsproving that afalsecommand returns exit code1.Validation
CARGO_TARGET_DIR=/tmp/codex-target-15536 cargo test -p codex-exec exits_non_zero_when_shell_command_finishes_failed -- --nocapturejust fix -p codex-execCARGO_TARGET_DIR=/tmp/codex-target-15536 cargo test -p codex-execRisk
Maintainer Pickup