Windows command execution can hang indefinitely when descendant processes inherit stdout/stderr handles, causing agent turns to become stuck forever
What version of Codex CLI is running?
codex-cli 0.145.0-alpha.18
What subscription do you have?
Pro
Which model were you using?
gpt-5.6-sol
What platform is your computer?
Microsoft Windows NT 10.0.26100.0 x64
What terminal emulator and version are you using (if applicable)?
_No response_
Codex doctor report
What issue are you seeing?
Codex command execution can remain active indefinitely on Windows after the launched parent process has exited or been terminated by its configured timeout, causing the agent to get stuck forever.
I experience this bug regularly and have to manually interrupt the turn and tell the agent it got stuck. Orphaned worker processes also accumulate on the system. Any build system, test runner, compiler daemon, development server, or script that spawns descendants may trigger the same problem.
This occurs when the command spawns descendant processes that inherit the command’s stdout/stderr pipe handles. Codex appears to wait for EOF from those pipes before completing the command execution. Because the descendants still hold the write ends open, EOF never arrives—even though the original process is no longer running and the shell-command timeout has expired.
This affects the shared Codex process-execution/app-server layer. It is observable in both the CLI and Codex Desktop because Desktop delegates command execution to app-server.
dotnet test is a reliable reproducer because it can spawn persistent MSBuild worker nodes, but the underlying issue applies to any program that leaves handle-inheriting descendants running.
What steps can reproduce the bug?
Reliable reproduction using .NET/MSBuild
- Open a repository containing a .NET test project.
- Ask Codex to execute a command such as:
dotnet test path\to\Tests.csproj -c Release - Configure a finite shell-command timeout.
- Have the command time out, be cancelled, or otherwise cause the parent
dotnet testprocess to exit while its spawned MSBuild worker nodes remain running. - Inspect the process list and observe descendant processes resembling:
dotnet.exe ... MSBuild.dll /nodemode:1 ... - Observe the Codex command execution after the parent process has exited.
General reproduction condition
The same failure should be reproducible with any command that:
- Spawns a descendant process.
- Allows that descendant to inherit stdout or stderr.
- Exits or is terminated while the descendant remains alive.
What is the expected behavior?
When a command reaches its timeout or is cancelled, Codex should:
- Terminate the entire process tree associated with the command.
- Close or detach all harness-owned stdout/stderr pipe handles deterministically.
- Return a timeout or cancellation result promptly.
- Avoid making command completion depend indefinitely on EOF from surviving descendants.
A configured timeout should be an upper bound, apart from a short and separately bounded cleanup interval.
When a spawned process ends but child processes continue to hold parent pipe handles indefinitely, the agent should ideally be notified so that it can choose to either continue waiting for the child processes or evaluate the situation and take appropriate action in the case where there are orphaned processes that need to be forcibly terminated.
Additional information
On Windows, consider running each command inside a Job Object configured to terminate contained processes when the job is closed, such as with JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE.
On timeout, cancellation, client disconnection, or app-server shutdown:
- Terminate or close the command’s Job Object so the complete process tree is stopped.
- Close the server’s command-specific pipe handles explicitly.
- Bound any stream-drain or process-cleanup wait.
- Return the command result without waiting indefinitely for EOF from uncontrolled descendants.
It may also be worth auditing handle inheritance so only intended child processes receive stdout/stderr handles, but that may not be viable.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗