[BUG] Bash tool does not propagate signals to child process tree, causing orphaned background processes

Resolved 💬 3 comments Opened Apr 2, 2026 by TidiBuddy Closed Apr 6, 2026

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

  1. 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"

  1. Execute it via the Claude Code Bash tool multiple times
  2. 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:

  1. The shell's EXIT trap is triggered and cleanup logic executes
  2. All child/background processes are terminated along with the parent
  3. No orphaned processes remain after the Bash tool finishes or cancels a command
  4. 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

  1. 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"

  1. Run it via Claude Code Bash tool: ask Claude to execute bash test-orphan.sh
  2. Repeat 3-5 times (or let the Bash tool timeout/cancel mid-execution)
  3. Check for orphaned processes: ps aux | grep test-orphan
  4. 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_

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗