Summary
Since v1.12, a sufficiently heavy Run Codex review step never completes on Linux. (See the control run in the comments: light runs on v1.12 finish fine, and v1.11 handles the same heavy workload cleanly.) Codex finishes the turn, writes its output file, prints its final message and token count — and then the step sits idle until the job's timeout-minutes kills it. The run is billed and discarded even though the result was already on disk.
The same workflow on v1.11 (52fe01ec70a42f454c9d2ebd47598f9fd6893d56) completes cleanly in ~87s.
Timeline from one affected run
ubuntu-latest, GitHub-hosted, runner 2.336.0, openai/codex-action@v1 resolving to 86365089eb2b84e0a8fb0717b304f8bdcb13b20e (v1.12):
| Time (UTC) |
Event |
| 06:40:48 |
job starts |
| 06:40:56 |
Run Codex review starts |
| 06:47:45 |
last exec completes |
| 06:47:51 |
final assistant message, then tokens used / 270,199 |
| 06:47:51 → 06:55:48 |
nothing at all |
| 06:55:48 |
job timeout-minutes: 15 deadline |
| 07:00:49 |
force-killed, job cancelled |
The step produced its full result in 6m55s, then idled for 7m57s.
The tell is the missing end marker. On v1.11 the log reads:
<token count>
##[end-action id=__openai_codex-action.run_codex;outcome=success;conclusion=success;duration_ms=86604]
On v1.12 the token count is the last line the step ever emits. No end-action, no outcome.
Configuration
- uses: openai/codex-action@v1
with:
openai-api-key: ${{ secrets.OPENAI_API_KEY }}
prompt-file: <path>
output-file: <path>
working-directory: <path>
model: gpt-5.6-luna
effort: xhigh
sandbox: read-only
output-schema-file: <path>
Raising timeout-minutes does not help — the step idles longer. Lowering effort does not help either; the turn was never the slow part.
Why this loses completed work
With an explicit output-file, runCodexExec passes --output-last-message <file> straight to codex exec, so the CLI writes the result itself as soon as the turn ends. In the run above the file existed at 06:47:51.
But because the step never returns, every later step in the job is skipped — including the artifact upload that hands the result to the next job. A finished, valid, schema-conforming result is thrown away. Consumers that treat step success as "a result exists" will silently produce nothing.
Suspected area
v1.12 is the first release to route Codex through the rewritten privilege-isolation path (src/dropSudo.ts +419/-20, src/runCodexExec.ts +285/-6, new src/linuxCredentials.ts), launching via:
/usr/bin/setpriv --reuid="$uid" --regid="$nobody_gid" --clear-groups \
--no-new-privs --bounding-set=-all --inh-caps=-all --ambient-caps=-all \
-- /usr/bin/env ... "$@"
exec "$@"
A plausible mechanism is a descendant process surviving the turn and holding the inherited stdio pipe open, so the wrapper never observes stream close. Supporting detail: even on a clean v1.11 run, the runner's own cleanup reports leftovers —
Cleaning up orphan processes
Terminate orphan process: pid (2460) (bash)
Terminate orphan process: pid (2462) (MainThread)
That run was 87s and spawned few children, so it exited regardless. The affected run executed many subprocesses during the turn.
A second symptom from the same run may share a cause: subprocesses failing to create a temporary directory. Retracted — a v1.11 control run produces the same errors and finishes normally, so that is ordinary sandbox: read-only behaviour and unrelated to this issue. See the comment below.
Possibly related open issues: #103 (output-schema with runAsUser fails to create temporary file), #137 (failed runs leave temporary output directories behind), #108 (proxy hangs).
Two things that would help regardless of the root cause
- Bound the wait. If the wrapper waited on process exit with a timeout rather than on stdio close, a lingering descendant would cost seconds, not the whole job budget.
- Don't hide a written result. The output file is complete before the hang. Failing the step with a message naming the written file — instead of never returning — would let callers publish the result they already paid for.
Workaround
Pin to v1.11:
uses: openai/codex-action@52fe01ec70a42f454c9d2ebd47598f9fd6893d56 # v1.11
Note that v1 currently points at v1.12, so anyone on the floating tag picked this up automatically.
Summary
Since
v1.12, a sufficiently heavyRun Codex reviewstep never completes on Linux. (See the control run in the comments: light runs on v1.12 finish fine, and v1.11 handles the same heavy workload cleanly.) Codex finishes the turn, writes its output file, prints its final message and token count — and then the step sits idle until the job'stimeout-minuteskills it. The run is billed and discarded even though the result was already on disk.The same workflow on
v1.11(52fe01ec70a42f454c9d2ebd47598f9fd6893d56) completes cleanly in ~87s.Timeline from one affected run
ubuntu-latest, GitHub-hosted, runner2.336.0,openai/codex-action@v1resolving to86365089eb2b84e0a8fb0717b304f8bdcb13b20e(v1.12):Run Codex reviewstartsexeccompletestokens used/270,199timeout-minutes: 15deadlinecancelledThe step produced its full result in 6m55s, then idled for 7m57s.
The tell is the missing end marker. On v1.11 the log reads:
On v1.12 the token count is the last line the step ever emits. No
end-action, nooutcome.Configuration
Raising
timeout-minutesdoes not help — the step idles longer. Loweringeffortdoes not help either; the turn was never the slow part.Why this loses completed work
With an explicit
output-file,runCodexExecpasses--output-last-message <file>straight tocodex exec, so the CLI writes the result itself as soon as the turn ends. In the run above the file existed at 06:47:51.But because the step never returns, every later step in the job is skipped — including the artifact upload that hands the result to the next job. A finished, valid, schema-conforming result is thrown away. Consumers that treat step success as "a result exists" will silently produce nothing.
Suspected area
v1.12is the first release to route Codex through the rewritten privilege-isolation path (src/dropSudo.ts+419/-20,src/runCodexExec.ts+285/-6, newsrc/linuxCredentials.ts), launching via:A plausible mechanism is a descendant process surviving the turn and holding the inherited stdio pipe open, so the wrapper never observes stream close. Supporting detail: even on a clean v1.11 run, the runner's own cleanup reports leftovers —
That run was 87s and spawned few children, so it exited regardless. The affected run executed many subprocesses during the turn.
A second symptom from the same run may share a cause: subprocesses failing to create a temporary directory.Retracted — a v1.11 control run produces the same errors and finishes normally, so that is ordinarysandbox: read-onlybehaviour and unrelated to this issue. See the comment below.Possibly related open issues: #103 (
output-schemawithrunAsUserfails to create temporary file), #137 (failed runs leave temporary output directories behind), #108 (proxy hangs).Two things that would help regardless of the root cause
Workaround
Pin to v1.11:
Note that
v1currently points at v1.12, so anyone on the floating tag picked this up automatically.