codex exec "<prompt>" hangs indefinitely when stdin is a non-TTY pipe with no writer

Open 💬 3 comments Opened May 4, 2026 by alexanderaperry-arch

Summary
When codex exec is invoked with a prompt as an argument from a non-interactive child shell (e.g. a CI runner, an editor's task runner, or another agent that spawns child processes), it hangs forever waiting for stdin EOF — even though the prompt was passed via the positional argument and stdin is never used.

This is a deterministic deadlock, not a flake. Reproducible on Windows 11 with codex-cli 0.128.0 and prior versions.

Repro

Inside a non-interactive child shell (here: a parent process spawning powershell.exe with piped stdio):

& codex exec "Reply OK only."

Hangs. Process has 0% CPU. No docker.exe MCP child spawned. No rollout

file written under ~/.codex/sessions. Stays hung until manually killed.

The same call with stdin redirected from NUL completes in ~11 s with normal output:

cmd /c "codex exec "Reply OK only." < NUL"

OK

tokens used: 19,542

Diagnosis
Stack inspection (no MCP child, 0 CPU, no rollout output, only stderr line Reading additional input from stdin...) plus --help text confirm that exec reads stdin when it is detected as a non-TTY pipe and appends the contents as a <stdin> block after the prompt argument:

If stdin is piped and a prompt is also provided, stdin is appended as a <stdin> block

In a non-TTY child shell with inherited but unwritten stdio, read() on stdin blocks indefinitely because the writer side is open but no bytes ever arrive and no EOF is delivered.

Why this matters
Standard for tools that spawn codex exec programmatically (Claude Code, GitHub Actions runners, nohup, Start-Process without -RedirectStandardInput NUL, agent orchestrators).
The codex app-server path (used by the Claude Code Codex plugin) is unaffected because the plugin actively writes JSONL and closes stdin.
Indistinguishable from a real hang/network problem from the caller's perspective — no logs, no rollout, no exit code, just silence.
Common mitigations (timeouts, retries) make the symptom worse: the next retry hangs the same way.
Proposed fix (one of)
When a prompt argument is present and stdin is a non-TTY pipe, only read stdin if data is actually available within a short timeout (e.g. poll for readability for 200 ms; if nothing, treat as empty and proceed).
Add an explicit --no-stdin flag (or invert the default to require - for stdin reads), so non-interactive callers can opt out cleanly.
Document the failure mode and the workaround prominently in codex exec --help, and emit a stderr warning after, say, 5 s of waiting so the deadlock is at least detectable.
Option 1 is the friendliest default and matches what most callers expect when they pass a prompt argument. Option 2 is the smallest behavior change.

Workaround (current)
POSIX shells: codex exec "<prompt>" < /dev/null
Windows cmd: codex exec "<prompt>" < NUL
Windows PowerShell child: cmd /c "codex exec "<prompt>" < NUL" (PowerShell has no clean pipe-to-NUL syntax)
Or wrap once: a codex-safe.cmd shim that forwards args and redirects from NUL.
Environment
Windows 11 26200 (Home)
codex-cli 0.128.0 (npm install path: C:\Users\<u>\AppData\Roaming\npm\codex.cmd → node_modules\@openai\codex-win32-x64\vendor\x86_64-pc-windows-msvc\codex\codex.exe)
Reproduces from PowerShell child invoked by Claude Code's Bash tool (PowerShell runs & codex exec "<prompt>" with inherited piped stdio)
Also reproduces from Start-Process powershell -ArgumentList ... without -RedirectStandardInput NUL

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗