claude-code icon indicating copy to clipboard operation
claude-code copied to clipboard

[BUG] File Descriptor Leak in FSWatcher for pending-claude-sessions.json Causes EINVAL Crash

Open john-savepoint opened this issue 1 month ago • 1 comments

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?

Claude Code leaks file descriptors when watching pending-claude-sessions.json during atomic write operations. The FSWatcher attaches to temporary .tmp files that are immediately renamed/deleted, leaving orphaned file handles. These accumulate over time (42,000+ in a single session) and eventually cause unhandled promise rejection crashes with EINVAL: invalid argument, watch.

What Should Happen?

  • File watchers should be properly closed when the watched file is renamed or deleted
  • Atomic write operations should not leave orphaned file descriptors
  • The FSWatcher should watch the final file path, not the intermediate .tmp file
  • EINVAL errors should be handled gracefully instead of crashing the session

Error Messages/Logs

This error originated either by throwing inside of an async function without a catch block,
  or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason:
  Error: EINVAL: invalid argument, watch '/home/user/.claude/pending-claude-sessions.json.tmp'
      at watch (unknown)
      at new FSWatcher (node:fs:30:31)
      at watch (node:fs:300:23)
      at tMI (/$bunfs/root/claude:389:6265)
      at Tv0 (/$bunfs/root/claude:389:11478)
      at _watchWithNodeFs (/$bunfs/root/claude:389:6746)
      at _handleFile (/$bunfs/root/claude:389:7489)
      at _addToNodeFs (/$bunfs/root/claude:389:10516)
      at processTicksAndRejections (native:7:39)

  Evidence of file descriptor leak (lsof output showing 42,777 orphaned FDs):

  claude  2487674  user  19r  REG  8,48  0  181658  .claude/pending-claude-sessions.json.tmp (deleted)
  claude  2487674  user  21r  REG  8,48  0  181654  .claude/pending-claude-sessions.json.tmp (deleted)
  claude  2487674  user  24r  REG  8,48  0  181640  .claude/pending-claude-sessions.json.tmp (deleted)
  ... (42,000+ more entries all marked "(deleted)")

Steps to Reproduce

  1. Run Claude Code with claude --dangerously-skip-permissions
  2. Use the session normally for 10-30+ minutes
  3. Monitor file descriptors with: lsof 2>/dev/null | grep "claude.*pending-claude.*deleted" | wc -l
  4. Observe orphaned FD count growing continuously (~1,200 leaked FDs per minute)
  5. Eventually session crashes with EINVAL error

Claude Model

Sonnet (default)

Is this a regression?

I don't know

Last Working Version

No response

Claude Code Version

2.0.76 (Claude Code)

Platform

Anthropic API

Operating System

Windows

Terminal/Shell

VS Code integrated terminal

Additional Information

Root cause analysis:

  1. Claude uses atomic writes (write to .tmp, then rename to final file)
  2. FSWatcher incorrectly attaches to the .tmp file
  3. When .tmp is renamed/deleted, the file descriptor is NOT closed
  4. Each atomic write cycle creates another orphaned FD
  5. Operations on orphaned FDs eventually throw EINVAL

Impact:

  • Session crashes from unhandled promise rejections
  • Memory/kernel resource consumption from leaked FDs
  • Affects users running multiple concurrent sessions (valid use case)
  • Makes long-running sessions unstable

Related issues:

  • #7624 (ENOSPC from settings file watcher - similar FSWatcher mechanism)
  • #6598 (EINVAL write error - different root cause but same error code)
  • #10505 (Memory leak in write loop - similar resource leak pattern)

Suggested fix:

  • Watch the directory or final file path, not the .tmp intermediate
  • Properly close file watchers when watched file is renamed/deleted
  • Handle EINVAL gracefully instead of crashing

john-savepoint avatar Dec 24 '25 01:12 john-savepoint

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/15073
  2. https://github.com/anthropics/claude-code/issues/14438
  3. https://github.com/anthropics/claude-code/issues/15068

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

github-actions[bot] avatar Dec 24 '25 01:12 github-actions[bot]

This issue has been automatically closed as a duplicate of #15073.

If this is incorrect, please re-open this issue or create a new one.

🤖 Generated with Claude Code

github-actions[bot] avatar Dec 27 '25 09:12 github-actions[bot]

This issue was actually rectified. There was a problem with custom scripts in my Tmux that was referencing Claude Code too frequently, and it had been imported cross-platform incorrectly. So this one's on me, sorry.

john-savepoint avatar Dec 31 '25 05:12 john-savepoint

This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.

github-actions[bot] avatar Jan 07 '26 14:01 github-actions[bot]