[BUG] Bash tool does not propagate signals to child process tree, causing orphaned background processes
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
Bug Description
When a script executed via the Bash tool contains background subprocesses (e.g., a timer using { while true; do sleep 1; done } &), and the script
registers a cleanup trap (trap 'cleanup_fn' EXIT), the trap is not triggered when the Bash tool terminates the process. This causes background child
processes to become orphaned and accumulate.
Reproduction Steps
- Create a script with a background subprocess and EXIT trap:
#!/usr/bin/env bash
_TIMER_PID=""
cleanup() { [ -n "$_TIMER_PID" ] && kill "$_TIMER_PID" 2>/dev/null; }
trap 'cleanup' EXIT
{ while true; do echo "tick" >&2; sleep 1; done } &
_TIMER_PID=$!
sleep 5
echo "done"
- Execute it via the Claude Code Bash tool multiple times
- Observe orphaned sleep/bash processes accumulating:
ps aux | grep "tick"
Expected Behavior
- The Bash tool should send signals (SIGTERM/SIGHUP) to the entire process group (e.g., kill -TERM -$PGID), so that the shell's EXIT trap is
triggered and child processes are properly cleaned up.
Actual Behavior
- The parent bash process is terminated, but the EXIT trap does not fire.
- Background child processes survive as orphans (PPID becomes 1).
- The Claude Code status bar shows an increasing "N shells" count.
- The orphaned processes persist until manually killed via pkill.
Environment
- Claude Code CLI version: 2.1.90
- OS: macOS 26.4 (Darwin 25.4.0, Apple M2)
- Shell: zsh 5.9
Additional Context
Running the exact same script directly in a terminal works correctly — the EXIT trap fires and all child processes are cleaned up. The issue is
specific to the Bash tool's process lifecycle management.
What Should Happen?
The Bash tool should send signals (SIGTERM/SIGHUP) to the entire process group (e.g. kill -TERM -$PGID) when terminating a command, so that:
- The shell's EXIT trap is triggered and cleanup logic executes
- All child/background processes are terminated along with the parent
- No orphaned processes remain after the Bash tool finishes or cancels a command
- The "N shells" counter in the status bar stays accurate
This is the standard behavior when running the same script directly in a terminal — the EXIT trap fires and all child processes are cleaned up.
Error Messages/Logs
Steps to Reproduce
- Create a script (
test-orphan.sh) that spawns a background subprocess with an EXIT trap:
#!/usr/bin/env bash
_PID=""
cleanup() { [ -n "$_PID" ] && kill "$_PID" 2>/dev/null; }
trap 'cleanup' EXIT
{ while true; do sleep 1; done } &
_PID=$!
sleep 5
echo "done"
- Run it via Claude Code Bash tool: ask Claude to execute
bash test-orphan.sh - Repeat 3-5 times (or let the Bash tool timeout/cancel mid-execution)
- Check for orphaned processes:
ps aux | grep test-orphan - Observe the Claude Code status bar showing accumulated "N shells"
Claude Model
None
Is this a regression?
Yes, this worked in a previous version
Last Working Version
_No response_
Claude Code Version
Claude Code CLI version: 2.1.90
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Terminal.app (macOS)
Additional Information
_No response_
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗