Background task respawns infinitely when command hangs, causing fork bomb
Bug Report
Claude Code version: 2.1.74
OS: Fedora 43 (Linux 6.19.8)
Node: v24.13.1
Description
When a background Bash task (run_in_background: true) hangs due to an external dependency failure, Claude Code respawns the command infinitely, creating a fork bomb. In our case this consumed 1,300+ bash processes, 41GB of 64GB RAM, and would have triggered the Linux OOM killer and required a hard reboot if not caught manually.
Steps to Reproduce
- Run a Bash tool call with
run_in_background: truethat pipes throughgrep/headand depends on an external connection:
````
adb shell "uiautomator dump /sdcard/ui.xml 2>&1 && cat /sdcard/ui.xml" 2>&1 | grep -oP '<node[^>]*text="(Pair|Cancel)"[^>]*>' | head -5
- The external connection (wireless ADB) drops mid-command
- The pipe chain breaks, the shell exits with an error
- Claude Code respawns the command — repeatedly and without limit
Observed Behavior
The spawned command template:
/bin/bash -c source ~/.claude/shell-snapshots/snapshot-bash-{timestamp}-{id}.sh \
&& shopt -u extglob 2>/dev/null || true \
&& eval "{escaped_command}" \
&& pwd -P >| /tmp/claude-{session_id}-cwd
What happens on failure:
- The ADB connection drops →
adb shellhangs or errors → pipe breaks → shell exits pwd -P >| /tmp/claude-*-cwdnever executes (command failed before reaching it)- Claude Code's Node.js process detects the failure and respawns the command
- Each respawn sources a ~247KB shell snapshot (full environment: nvm, systemd, bash functions)
- New processes immediately fail the same way → respawn loop
Memory per process (measured):
- Virtual: 228MB (VmSize)
- Resident: 5.4MB (VmRSS) — grows with ADB client + pipe buffers
- At 1,300 processes: ~7GB RSS minimum, ~290GB virtual reservation
Timeline of escalation:
- Initial state: ~9GB RAM used, 650 processes
- Within minutes: 2,000+ processes, 41GB RAM, load average 58
sudo pkill -9 -f "snapshot-bash"— kills existing processes, new ones immediately respawn- Deleting/overwriting the snapshot .sh file — processes still respawn
- If unchecked: fills all 64GB RAM + 8GB swap → Linux OOM killer → system freeze → hard reboot required
No user-accessible way to stop it:
| Recovery attempt | Result |
|---|---|
| Reject the tool call in UI | Does NOT stop the background task |
| /clear | Does NOT stop the background task |
| TaskStop tool | "No task found" — Bash background tasks aren't registered as stoppable tasks |
| sudo pkill -9 -f "snapshot-bash" | Kills processes, but Claude Code immediately respawns them |
| Delete/overwrite snapshot .sh file | Processes still respawn (Node.js re-creates them) |
| kill -9 <claude_pid> | Only working fix — kills the parent Node.js process |
Root Cause Analysis
We traced through the minified cli.js source:
- Background Bash commands are spawned via Node.js
child_process.spawn()withdetached: trueandstdioredirected to a task output file - The process lifecycle is managed by class
cK8, which listens forexitanderrorevents - The
cK8class itself does not contain retry logic — it resolves its promise on exit - However, something upstream in the tool execution pipeline re-triggers the spawn when the process exits with a failure code. The exact respawn mechanism is unclear from the minified source, but the behavior is deterministic: every
pkillis followed by immediate respawn of the same command
The shell snapshot (snapshot-bash-*.sh) is a 247KB file containing base64-encoded bash functions (nvm completions, systemd OSC sequences, etc.) that gets sourced on every spawn, adding overhead to each fork.
Expected Behavior
- Retry limit: Background tasks should cap retries (e.g., max 3) before giving up
- Tool rejection = task cancellation: Rejecting a tool call should cancel any associated background process
/clearshould kill background tasks: Or at minimum, provide/kill-backgroundcommandTaskStopshould work for Bash tasks: Background Bash tasks should be registered in the task system- Graceful degradation: If a background task fails repeatedly, notify the user instead of silently respawning
Workarounds
Until this is fixed, users can protect themselves:
Option 1: Environment variable (disables all background tasks globally)
# Add to ~/.bashrc or ~/.zshrc
export CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1
This removes the run_in_background parameter from the Bash tool schema entirely — Claude cannot use it.
Option 2: PreToolUse hook (blocks background Bash in specific projects)
Add to .claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "echo \"$CLAUDE_TOOL_INPUT\" | grep -q '\"run_in_background\"\\s*:\\s*true' && echo 'BLOCK: Background bash tasks disabled — fork bomb risk.' >&2 && exit 2 || exit 0"
}
]
}
]
}
}
If you're already in the fork bomb state:
# Find and kill the Claude Code process (the Node.js parent)
ps aux | grep claude | grep -v grep
kill -9 <claude_pid>
# Then clean up any remaining orphaned processes
sudo pkill -9 -f "snapshot-bash"
Impact
Severity: Critical — This is a silent, unrecoverable resource exhaustion bug. The user gets no warning. The only visible symptom is the system slowing down. By the time they notice, thousands of processes may already be running. On systems without swap or with smaller RAM, this would trigger OOM and kill unrelated processes (browsers, IDEs, unsaved work).
This issue has 8 comments on GitHub. Read the full discussion on GitHub ↗