Background tasks with piped output don't clean up process tree on timeout
Resolved 💬 5 comments Opened Jan 6, 2026 by babalu27 Closed Feb 27, 2026
Description
When running background bash commands that pipe output through head, tail, or similar, if the TaskOutput times out, the entire process tree is not killed. This leads to zombie processes accumulating.
Environment
- OS: macOS (Darwin 25.1.0)
- Shell: zsh
- Terminal: WezTerm
- Claude Code Version: 2.0.76
Steps to Reproduce
- Run a long-running command in background with piped output:
# Using run_in_background: true
cargo build --release 2>&1 | tail -30
- TaskOutput times out waiting for the result
- Check for zombie processes:
ps aux | grep cargo
Expected Behavior
When TaskOutput times out, the entire process tree (parent shell, cargo, and tail/head) should be killed.
Actual Behavior
The processes remain running indefinitely:
rquirch 17197 /bin/zsh -c ... cargo check 2>&1 | head -20
rquirch 16041 /bin/zsh -c ... cargo zigbuild ... | tail -30
Multiple background tasks accumulate as zombies.
Root Cause Analysis
- Cargo outputs progress to stderr (line-buffered to TTY, but fully-buffered when piped)
- The pipe fills slowly because cargo is compiling/downloading
head/tailblocks waiting for buffered input- TaskOutput timeout doesn't kill the process group/tree
- Zombie processes accumulate
Workaround
- Don't pipe output in background commands
- Use
stdbuf -oL -eLto force line buffering - Run long commands in foreground instead
Suggested Fix
When killing a background task on timeout, use kill -TERM -$PGID (process group) or recursively kill child processes to ensure the entire process tree is terminated.
This issue has 5 comments on GitHub. Read the full discussion on GitHub ↗