I called codex exec from an AI agent's tool runner, and no response ever came back. The hang I observed lasted more than 10 minutes. The prompt was passed as an argument, and there was nothing wrong with the command itself.

When you type the command by hand, stdin is connected to your terminal, so the wait never happens. The difference is how the caller connects stdin.

This post is for anyone who runs codex exec from scripts, CI, other programs, or AI agents. If you only use it from a terminal, you can skip it.

The Cause Is stdin

Looking into it in my environment (Codex CLI 0.164.0), I found that the agent's execution environment was handing codex exec a pipe as stdin.

OpenAI's official "Non-interactive mode" documentation specifies the following:

  • When stdin is piped and a prompt argument is also given, the argument is treated as the instruction and the piped input as additional context.
  • To make the whole of stdin the prompt, omit the argument or use codex exec -.
  • Progress goes to stderr, and the final message goes to stdout.

That much is the official spec. Everything below is based on my own observations. The official documentation does not mention the hang where EOF never arrives, nor any workaround (in the documentation I checked).

If the caller hands over a pipe and never closes it, Codex concludes that additional context is still on its way and keeps reading until EOF. EOF never comes. It waits, and it hangs.

The telltale sign is a single line on stderr. While the process was hung, this message appeared, and in our use it appeared every time:

Reading additional input from stdin... 

If no response comes back and nothing happens after this line, Codex may be waiting for EOF on stdin rather than processing the prompt. The rest of this post goes through what to check and what to do for each kind of caller.

The Fix: Make stdin Return EOF Immediately

In my environment, I ran the same call with stdin redirected from /dev/null, and a consultation task at an effort level equivalent to High finished in about 13 seconds. Since then I've appended < /dev/null to every call from this environment.

codex exec "review this" < /dev/null 

/dev/null returns EOF as soon as you read from it. Codex decides there is no additional context and moves on.

Piping input is not a problem in itself. As the official spec describes, I routinely pass command output as additional context:

git diff | codex exec "review this diff" 

The problem is stdin left open when you never intended to send any input, not the pipe itself. For every call, you have to decide whether stdin is something you hand over or something you close.

Workarounds by Caller

Here is what stdin looks like for each kind of caller, and what to do about it.

First, check what stdin is

Before fixing anything, find out where stdin is connected right now.

In a shell, test -t 0 tells you whether stdin is a terminal:

if test -t 0; then echo "stdin is a terminal"; else echo "stdin is not a terminal (pipe, file, etc.)"; fi 

If it says stdin is not a terminal, this hang is possible. When calling from a script or a program, check the launch configuration to see where stdin is connected: the child process's stdin setting, the job's execution settings, or the agent's tool-execution spec. The point is to run the check under the same conditions as the real caller. Even if a test in your terminal says "terminal," the result can differ when the call comes through an agent.

Typing it by hand in a terminal

When stdin is connected to a terminal, the EOF wait described here does not occur. You don't need to add anything.

Shell scripts

For scripts, also check how the launcher connects stdin. A script started by cron, a CI job, or another program may have something other than a terminal attached.

If you don't plan to pass anything in, add < /dev/null. If you do, make it explicit with a pipe:

codex exec "summarize this" < /dev/null cat report.txt | codex exec "summarize this" 

Launching as a child process (spawn, Popen, and the like)

When another program launches codex exec as a child process, check the stdin setting of the API you're using.

  • If you aren't passing anything, point stdin at /dev/null or close it right after launch.
  • If you are passing input, always close it after you finish writing.

If you write input but forget to close the stream, Codex keeps waiting, exactly as if you had passed nothing.

Calling from an AI agent's tool runner

This is the case from the opening. In my environment, the caller never closed the write end of the pipe, so EOF never arrived and the process kept waiting. It made no difference that the prompt was passed as an argument.

The fix is to always append < /dev/null to any call that doesn't pass input on stdin. When you delegate to an agent, writing "append < /dev/null to any codex exec that does not pass input on stdin" in the runbook or instructions keeps it from being forgotten.

This is the template I actually use for invocations:

codex exec \ --ephemeral \ -p consult \ --sandbox read-only \ "review this" < /dev/null 

I keep the non-interactive flag (--ephemeral) fixed in the template, and set the profile and sandbox on the calling side rather than leaving them to the agent. (The profile name and sandbox value are examples; adapt them to your own setup.) To prevent forgotten flags and drift, keep the template in one place and call it only from there. The one exception is a call that does pass input on stdin: there, you drop < /dev/null and use a pipe instead.

When You Intentionally Supply Input on stdin

There are two ways to use stdin on purpose, and it's important to distinguish them.

Form Syntax What stdin means
Instruction + additional context cmd | codex exec "instruction" The argument is the instruction; piped input is supplementary
All of stdin is the prompt codex exec - or no argument The piped input is the prompt itself

According to the official documentation, to make stdin the entire prompt you omit the argument or use codex exec -.

Either way, both forms require you to finish sending input and then close the stream. Once every writer has closed, EOF arrives and Codex stops waiting for input. A cmd | codex exec ... pipeline hangs only when a writer is left open.

Pre-Flight Checklist

Before calling codex exec from a new environment, check these three things:

  1. What is stdin? A terminal, a pipe, or /dev/null. Run test -t 0 and inspect the launch configuration to learn what the caller will hand you.
  2. Have you decided whether to pass it or close it? If you pass input, close it at the end. If you don't, append < /dev/null.
  3. If it hangs, look at stderr. If no response comes back and nothing happens after Reading additional input from stdin..., check whether EOF is actually reaching stdin.

According to the official documentation, progress goes to stderr during a run and the final answer goes to stdout, so be careful how you capture logs. If you throw stderr away, you won't see that line either. For unattended runs, record stderr too, so you can check afterward whether this message appeared.

The behavior and numbers described here are based on observations in my environment (0.164.0). They may change with the version, so when you set this up somewhere new, test once to confirm it doesn't hang.

Originally published in Japanese on note. This English edition was translated and edited with AI assistance; all commands and measurements are unchanged from the original.