Skip to content

fix: use Port with closed stdin to prevent Codex CLI hang#34

Merged
joshrotenberg merged 1 commit intomainfrom
fix/stdin-hang
Mar 31, 2026
Merged

fix: use Port with closed stdin to prevent Codex CLI hang#34
joshrotenberg merged 1 commit intomainfrom
fix/stdin-hang

Conversation

@joshrotenberg
Copy link
Copy Markdown
Collaborator

Closes #33. Redirects stdin from /dev/null via Port so Codex doesn't hang.

Codex CLI hangs when stdin is inherited from the parent process
(waiting for input that never comes). The Rust codex-wrapper solved
this with Stdio::null().

Replaced System.cmd with Port-based execution that redirects stdin
from /dev/null via shell wrapper. Handles shell escaping for args.

Closes #33
@joshrotenberg joshrotenberg merged commit 328ea18 into main Mar 31, 2026
2 checks passed
joshrotenberg added a commit that referenced this pull request Apr 10, 2026
The non-streaming execute path was fixed in #34, but the streaming
paths in Exec.stream/2 and ExecResume.stream/2 still opened a raw
Port with stdin inherited from the parent. Codex CLI 0.118+ reads
stdin at startup and blocks forever on the open-but-empty pipe,
causing streams to hang until the internal 300s safety timeout.

Extract the shell-wrapping logic from Command.run_with_closed_stdin
into a shared Command.shell_cmd_args/3 helper that both execute and
streaming paths now use. Streaming opts out of the 2>&1 stderr merge
so stderr flows to the parent without contaminating NDJSON on stdout.

Verified against codex-cli 0.119.0: Exec.stream/2 now completes in
~13s with the expected thread.started / turn.started / item.completed
/ turn.completed events instead of hanging on the safety timeout.

Closes #37
joshrotenberg added a commit that referenced this pull request Apr 10, 2026
The non-streaming execute path was fixed in #34, but the streaming
paths in Exec.stream/2 and ExecResume.stream/2 still opened a raw
Port with stdin inherited from the parent. Codex CLI 0.118+ reads
stdin at startup and blocks forever on the open-but-empty pipe,
causing streams to hang until the internal 300s safety timeout.

Extract the shell-wrapping logic from Command.run_with_closed_stdin
into a shared Command.shell_cmd_args/3 helper that both execute and
streaming paths now use. Streaming opts out of the 2>&1 stderr merge
so stderr flows to the parent without contaminating NDJSON on stdout.

Verified against codex-cli 0.119.0: Exec.stream/2 now completes in
~13s with the expected thread.started / turn.started / item.completed
/ turn.completed events instead of hanging on the safety timeout.

Closes #37
joshrotenberg added a commit that referenced this pull request Apr 10, 2026
The non-streaming execute path was fixed in #34, but the streaming
paths in Exec.stream/2 and ExecResume.stream/2 still opened a raw
Port with stdin inherited from the parent. Codex CLI 0.118+ reads
stdin at startup and blocks forever on the open-but-empty pipe,
causing streams to hang until the internal 300s safety timeout.

Extract the shell-wrapping logic from Command.run_with_closed_stdin
into a shared Command.shell_cmd_args/3 helper that both execute and
streaming paths now use. Streaming opts out of the 2>&1 stderr merge
so stderr flows to the parent without contaminating NDJSON on stdout.

Verified against codex-cli 0.119.0: Exec.stream/2 now completes in
~13s with the expected thread.started / turn.started / item.completed
/ turn.completed events instead of hanging on the safety timeout.

Closes #37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: Codex CLI hangs on exit — System.cmd never returns

1 participant