Skip to content

Commands hang indefinitely when timeout occurs on shell-wrapped processes #4337

@danielchristiancazares

Description

@danielchristiancazares

What version of Codex is running?

codex-cli 0.42.0

Which model were you using?

gpt-5-codex

What platform is your computer?

Linux 6.6.87.2-microsoft-standard-WSL2 x86_64 unknown

What steps can reproduce the bug?

When a shell tool call times out, Codex only kills the shell wrapper (bash -lc), not its child processes. These orphaned children keep stdout/stderr pipes open, even causing Codex to hang indefinitely in some cases despite the 366s wrapper timeout.

Steps to Reproduce

On any Linux machine:

  1. Launch Codex (CLI or desktop).
  2. Run (with a 5-second timeout): mkfifo pipe.$$; (while true; do echo child; sleep 1; done) > pipe.$$ & cat pipe.$$
  3. Wait for the five-second timeout. Codex logs that the tool call timed out, but the UI remains on "working".
  4. In another terminal, run ps -ef | grep '[p]ipe' (or inspect the loop). Both the background writer and cat are still running.

Observed Behavior

Even after the timeout, the child pipeline keeps running. Because those descendants keep the stdout pipe open, Codex's reader tasks never finish, so the turn remains stuck until you manually interrupt.

Expected Behavior

When a tool call times out, Codex should terminate the entire process tree so stdout and stderr close, the readers exit, and the tool call finishes cleanly.

Additional Notes

A real-world example is cargo test --release with a timeout. Cargo's child runners survive the timeout, continue emitting output, and Codex stays in "working" until you abort the turn.

Root Cause

  • Commands wrapped in bash -lc create a shell wrapper process.
  • On timeout, only the wrapper is killed via its PID.
  • Child processes become orphaned but keep running.
  • They hold stdout/stderr pipes open indefinitely.
  • Reader tasks block waiting for EOF that never arrives.

Fix

  • Use setsid() to create process groups.
  • Kill the entire process group with kill(-pid, signal).
  • Ensures all descendants terminate and pipes close properly.

Metadata

Metadata

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions