[Bug] statusline.js causes infinite loop and EBADF error in Claude Code

Resolved 💬 3 comments Opened Sep 23, 2025 by umiyosh Closed Sep 23, 2025

🐛 Bug Report

Summary

When statusLine configuration is set in ~/.claude/settings.json, Claude Code enters an infinite loop and crashes with an EBADF error.

Environment

  • Claude Code Version: 1.0.85+
  • Node Version: v24.5.0
  • Platform: macOS (Darwin)
  • Installation: via nodebrew

Configuration

{
  "statusLine": {
    "type": "command",
    "command": "~/.claude/statusline.js"
  }
}

Error Output

node:internal/child_process:421
    throw new ErrnoException(err, 'spawn');
    ^

Error: spawn EBADF
    at ChildProcess.spawn (node:internal/child_process:421:11)
    at spawn (node:child_process:795:9)
    at file:///Users/a14073/.nodebrew/node/v24.5.0/lib/node_modules/@anthropic-ai/claude-code/cli.js:165:2705
    at TA0 (file:///Users/a14073/.nodebrew/node/v24.5.0/lib/node_modules/@anthropic-ai/claude-code/cli.js:165:3200)
    at file:///Users/a14073/.nodebrew/node/v24.5.0/lib/node_modules/@anthropic-ai/claude-code/cli.js:165:3433
    at Array.forEach (<anonymous>)
    at ChildProcess.W (file:///Users/a14073/.nodebrew/node/v24.5.0/lib/node_modules/@anthropic-ai/claude-code/cli.js:165:3368)
    at ChildProcess.emit (node:events:507:28)
    at ChildProcess.emit (node:domain:489:12)
    at maybeClose (node:internal/child_process:1101:16) {
  errno: -9,
  code: 'EBADF',
  syscall: 'spawn'
}

Node.js v24.5.0

Analysis of the Problem

The EBADF (Bad File Descriptor) error typically indicates one of the following issues:

  1. File descriptor exhaustion: The statusline.js script spawns multiple child processes via execSync() for git commands (lines 426-436, 626-641, etc.). If these are called frequently without proper cleanup, file descriptors may be exhausted.
  1. Recursive spawn issue: The error stack trace shows the crash occurs during ChildProcess.spawn, suggesting a possible recursive spawning issue where Claude Code repeatedly attempts to execute the statusline command.
  1. Resource leak in child processes: The statusline.js uses execSync() with various git commands. If these commands fail or timeout repeatedly, they might leave zombie processes or unclosed file descriptors.

Potential Root Causes

Looking at the statusline.js implementation:

  1. Multiple execSync calls: The script makes numerous synchronous shell calls:
  • Git status checks (lines 626-641)
  • Git branch information (line 632)
  • Git status porcelain v2 (line 638)
  • Claude version detection (line 433)
  1. File reading operations: Multiple file read operations for:
  • Transcript files (line 256)
  • Difficulty files (lines 324-336)
  • Package.json for version detection (lines 416-429)
  • Opus4 usage tracking (line 459)
  1. Stream handling: The script uses readline streams (lines 257-290, 543-620) which might not be properly closed in error scenarios.

Reproduction Steps

  1. Set the statusLine configuration in ~/.claude/settings.json as shown above
  2. Start Claude Code
  3. Claude Code enters an infinite loop and crashes with EBADF error

Expected Behavior

The statusline should display properly without causing Claude Code to crash.

Actual Behavior

Claude Code enters an infinite loop and terminates with an EBADF error.

Possible Solutions

  1. Add proper error handling and resource cleanup:
  • Ensure all streams are properly closed
  • Add try-catch blocks around execSync calls
  • Implement timeout handling for git commands
  1. Reduce subprocess spawning:
  • Cache git information for a short period
  • Batch git commands where possible
  • Use native Node.js git libraries instead of spawning processes
  1. Implement rate limiting:
  • Add a minimum interval between statusline updates
  • Implement debouncing for rapid calls
  1. File descriptor management:
  • Explicitly close file descriptors after use
  • Monitor and limit concurrent subprocess spawns

Workaround

Remove or comment out the statusLine configuration from ~/.claude/settings.json until the issue is resolved.

Additional Context

The statusline.js script is a custom implementation that displays various information including:

  • Git branch and status
  • Token usage tracking
  • Task difficulty indicators
  • Opus4 model usage tracking

The script works independently when tested outside of Claude Code, suggesting the issue is in the interaction between Claude Code's spawning mechanism and the script's subprocess calls.

Labels

  • bug
  • critical
  • statusline
  • resource-leak

View original on GitHub ↗

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