`write_stdin` fails on non-TTY sessions – no way to pipe input without a full PTY
What version of Codex CLI is running?
rust-v0.117.0 (via codex-acp v0.11.1)
What subscription do you have?
Pro
Which model were you using?
_No response_
What platform is your computer?
Darwin 25.3.0 arm64 arm
What terminal emulator and version are you using (if applicable)?
_No response_
What issue are you seeing?
What happened
When the agent calls exec_command with tty=false (the default) and later tries to send input to the running process via write_stdin, it immediately fails with:
stdin is closed for this session; rerun exec_command with tty=true to keep stdin open
This came up multiple times over a ~12 hour agent session:
[agent:codex-acp] 2026-04-18T21:31:50.928396Z ERROR codex_core::tools::router:
error=write_stdin failed: stdin is closed for this session;
rerun exec_command with tty=true to keep stdin open
[agent:codex-acp] 2026-04-18T23:54:27.017650Z ERROR codex_core::tools::router:
error=write_stdin failed: stdin is closed for this session;
rerun exec_command with tty=true to keep stdin open
[agent:codex-acp] 2026-04-19T10:13:45.023769Z ERROR codex_core::tools::router:
error=write_stdin failed: stdin is closed for this session;
rerun exec_command with tty=true to keep stdin open
What I expected
write_stdin should work on non-TTY sessions, at least when the process was spawned with a pipe. A full PTY shouldn't be required just to send input to stdin.
Why this happens
In process_manager.rs, write_stdin has a hard gate:
if !request.input.is_empty() {
if !tty {
return Err(UnifiedExecError::StdinClosed);
}
}
Non-TTY processes are spawned via spawn_process_no_stdin_with_inherited_fds, which uses PipeStdinMode::Null — stdin is literally /dev/null. So there's no pipe to write to.
The thing is, the infrastructure for a piped stdin already exists in pipe.rs: there's a PipeStdinMode::Piped variant and a spawn_process function that uses it. But process_manager.rs never takes that path — the branching is binary: full PTY or null stdin, nothing in between.
Impact
The agent can't pipe content to interpreters (python3 -, bash -s, node -e, etc.) without spinning up a full PTY. This matters because piping via stdin is the most reliable way to run multiline scripts — it avoids shell escaping issues with -c and doesn't need temp file writes that may hit sandbox restrictions.
Each failure wastes a tool call and tokens on a retry that the agent has to figure out on its own.
Platform
- Darwin 25.3.0 arm64 arm
- Bug observed via
codex-acpagent runtime - Namespace in logs:
codex_core::tools::router
What steps can reproduce the bug?
- Call
exec_commandwithtty=false(the default) — e.g. startpython3 -orcat - On the same session, call
write_stdinwith any non-empty input - Observe: immediately returns
stdin is closed for this session; rerun exec_command with tty=true to keep stdin open
What is the expected behavior?
write_stdin should succeed on non-TTY sessions when the process was spawned with a piped stdin. A full PTY shouldn't be required just to send input to a process.
Additional information
_No response_
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗