100% CPU spin after terminal disconnect - stdio revocation not handled

Resolved 💬 3 comments Opened Feb 1, 2026 by krushr1 Closed Feb 4, 2026

Bug Description

Claude Code spins at 100% CPU indefinitely after the parent terminal is closed, instead of gracefully exiting.

Environment

  • macOS (Apple Silicon)
  • Claude Code CLI
  • Node.js v22.16.0

Reproduction

  1. Start a Claude Code session in a terminal
  2. Close the terminal window (or let terminal crash)
  3. Observe orphaned claude process at 100% CPU with PPID=1

Evidence

Process stuck for nearly 3 hours at 100% CPU:

Process Name    % CPU    CPU Time      PID   PPID
claude          100.0    2:52:52.21    19090    1

lsof -p 19090 shows revoked file descriptors:

node    19090    0    (revoked)   ← stdin
node    19090    1    (revoked)   ← stdout
node    19090    2    (revoked)   ← stderr
node    19090   14    (revoked)   ← MCP pipe
node    19090   18    (revoked)   ← MCP pipe
node    19090   19    (revoked)   ← MCP pipe

Root Cause

When the terminal closes, macOS revokes the stdio file descriptors. The Node.js process continues running (orphaned to launchd) but every read/write to stdin/stdout returns immediately with EBADF/EPIPE. Without backoff or detection, this causes a tight loop at 100% CPU.

Suggested Fix

process.stdin.on('error', (err) => {
  if (err.code === 'EBADF' || err.code === 'EPIPE') {
    process.exit(0);  // Graceful exit on terminal disconnect
  }
});

process.stdout.on('error', (err) => {
  if (err.code === 'EPIPE') {
    process.exit(0);
  }
});

// Or handle SIGHUP properly
process.on('SIGHUP', () => {
  // Cleanup and exit
  process.exit(0);
});

Impact

  • Wastes CPU resources indefinitely
  • Can accumulate multiple zombie processes over time
  • Battery drain on laptops
  • Requires manual kill to clean up

View original on GitHub ↗

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