Windows command execution can hang indefinitely when descendant processes inherit stdout/stderr handles, causing agent turns to become stuck forever

Open
#33,913 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
45/100
Issue type
Bug
Clarity
Mostly clear
Activity status
Quiet
Tech stack
rust

Research direction

Start in the shared Codex process-execution/app-server layer described by the issue and reproduce the hang with dotnet test on Windows. Investigate process-tree cleanup, inherited stdout/stderr handles, and Windows Job Objects, then verify that timeout or cancellation returns promptly without waiting indefinitely for EOF or leaving descendant processes running.

Written by the indexing model from the issue text.

Description

app-server bug CLI tool-calls windows-os
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
  1. Open a repository containing a .NET test project.
  2. Ask Codex to execute a command such as: dotnet test path\to\Tests.csproj -c Release
  3. Configure a finite shell-command timeout.
  4. Have the command time out, be cancelled, or otherwise cause the parent dotnet test process to exit while its spawned MSBuild worker nodes remain running.
  5. Inspect the process list and observe descendant processes resembling: dotnet.exe ... MSBuild.dll /nodemode:1 ...
  6. Observe the Codex command execution after the parent process has exited.
General reproduction condition

The same failure should be reproducible with any command that:

  1. Spawns a descendant process.
  2. Allows that descendant to inherit stdout or stderr.
  3. 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:

  1. Terminate the entire process tree associated with the command.
  2. Close or detach all harness-owned stdout/stderr pipe handles deterministically.
  3. Return a timeout or cancellation result promptly.
  4. 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:

  1. Terminate or close the command’s Job Object so the complete process tree is stopped.
  2. Close the server’s command-specific pipe handles explicitly.
  3. Bound any stream-drain or process-cleanup wait.
  4. 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.

Dominant language
Rust
Stars
125k
Forks
19.5k
Avg merge
1m
Merged PRs (30d)
1k

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from openai/codex

All issues in openai/codex

Similar issues

More Rust issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.