Backgrounded sudo processes escape Bash tool timeout, causing unbounded disk growth (300GB+)

Resolved 💬 3 comments Opened Mar 1, 2026 by risenowrise Closed Mar 5, 2026

Backgrounded sudo processes escape Bash tool timeout/cleanup, causing unbounded disk usage

Summary

When the Bash tool runs a command that spawns a sudo-elevated background process, the process survives the tool's timeout and continues writing to the tool-results file indefinitely. In my case, a single fs_usage trace ran for 2.5 hours undetected and produced a 371 GiB tool-results file.

Reproduction

  1. In a Claude Code session, ask something like "find resource-hogging processes system-wide"
  2. Claude generates a command like:

``bash
sudo fs_usage -w -f filesys 2>/dev/null & PID=$!; sleep 3; kill $PID 2>/dev/null; wait $PID 2>/dev/null | sort | uniq -c | sort -rn | head -20
`
with a
timeout: 15000` (15s)

  1. The PID=$! assignment fails in zsh (command not found: PID=), so the kill is a no-op
  2. The 15s timeout fires, Claude's bash process exits, and ~30KB of stdout is captured successfully (interrupted: false)
  3. sudo fs_usage (owned by root, PID 1 parent) keeps running indefinitely, still writing to:

``
~/.claude/projects/<project>/<session>/tool-results/<id>.txt
``

  1. Claude analyzes the partial output and continues the conversation, unaware the process is still alive
  2. The tool-results file grows without bound — in my case to 371 GiB before I manually discovered and killed it

Root Cause

Three compounding issues:

1. No cleanup of orphaned child processes after timeout

When the Bash tool's timeout kills the shell, child processes spawned via sudo reparent to PID 1 (launchd/init). The tool has no mechanism to track or kill these orphans. Regular (non-sudo) background processes may also escape if they detach from the process group.

2. Shell syntax incompatibility (zsh)

The generated command uses & PID=$!; ... which doesn't work in zsh — the PID=$! is parsed as a separate command after the & and fails. This means the intended self-cleanup (kill $PID) never fires. Claude doesn't notice the error in stderr/stdout because it's mixed into the trace output.

3. No safeguard on tool-results file size

There is no monitoring, cap, or circuit breaker on tool-results files. A file can grow to hundreds of GiB with nothing flagging it. Even a simple periodic size check or a max file size limit (e.g., 100 MB) would prevent this class of failure.

Impact

  • Disk space: 371 GiB consumed silently by a single file in ~/.claude/
  • Backup bloat: My incremental backup (rustic/restic) jumped from ~300 GiB to ~570 GiB mid-run because of this file
  • CPU: fs_usage was running at 100% CPU for 2.5 hours
  • Silent failure: No warning, no error, no indication anything was wrong. The session continued normally.

Suggested Fixes

  1. Process group cleanup: Run Bash tool commands in their own process group (setsid or pgid). On timeout, kill the entire process group, not just the shell. For sudo children, consider sudo kill of known child PIDs.
  1. Tool-results file size limit: Cap tool-results files at a reasonable size (e.g., 50–100 MB). Truncate and warn if exceeded.
  1. Orphan process detection: After a Bash tool invocation completes, check if any child processes are still running and warn/kill them.
  1. Shell compatibility: Ensure generated commands work in both bash and zsh, or detect the user's shell and adapt. The & PID=$! pattern is a known zsh pitfall.

Environment

  • Claude Code version: 2.1.63
  • Shell: zsh
  • Platform: macOS (Darwin 25.2.0, Apple Silicon)
  • Model: claude-opus-4-6

View original on GitHub ↗

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