Long-running session exhausts macOS kernel file table (ENFILE) and locks the host
Summary
A long-running Claude Code session on macOS exhausted the system-wide
file descriptor table (kern.maxfiles, ENFILE — "too many open files in
system", not the per-process limit), rendering the entire Mac unusable
and requiring a hard power cycle to recover. Every other process began
failing simultaneously (btop, zsh powerlevel10k tmp writes, gcloud
auth helpers, etc.). This is the second occurrence in a few days on
the same machine.
Recovery cost was non-trivial: lost ~100 browser tabs, had to
re-auth Docker / gcloud / GitHub / every other tool, lost the
in-flight session.
Environment
- Claude Code CLI (bun-bundled — stack frames in
$bunfs/root/src/entrypoints/cli.js)
- macOS, Darwin 25.4.0
- Mac Studio, 256 GB unified memory; memory usage at crash was ~20% of
total. Not a memory pressure event.
- Session was an engineering workflow run with several background
subagents over its lifetime.
What I saw
Pane 1 (Claude Code crashing):
ERROR ENFILE: file table overflow, stat '/Users/randolph/.claude/.claude.json'
at statSync (unknown)
at <several frames> ($bunfs/root/src/entrypoints/cli.js:56:829)
($bunfs/root/src/entrypoints/cli.js:143:1055)
...
Pane 2 (fresh zsh, same instant):
zsh: too many open files in system: btopbtop
_p9k_dump_instant_prompt:sysopen:388: can't open file ~/.cache/p10k-randolph/prompt-33.tmp.6539: too many open files in system
_p9k_dump_state:sysopen:6: can't open file ~/.cache/p10k-dump-randolph.zsh.tmp.6539: too many open files in system
"too many open files in system" = kernel-wide ENFILE, not the
per-process EMFILE. Once that triggers, Activity Monitor / Console /lsof can't launch either — only the hardware power button works.
Hypothesis
The statSync('/Users/randolph/.claude/.claude.json') from the CLI
entrypoint is the call that finally tipped it over, not the source of
the leak. The underlying cause is fds being held open (or
created-without-closing) across a long session — likely some
combination of file watchers, background subagent / Monitor pipes that
outlive their parent, or stat-loop on .claude.json.
Asks
- Bound the fd count for background subagent / Monitor lifetimes so a
long session can't drain the kernel table.
- Sample the runtime's own fd count and warn the user before it
approaches the host limit, with a path to gracefully checkpoint.
- Catch ENFILE on the
.claude.jsonstat path and bail with state
preserved instead of crashing the runtime.
Happy to attach lsof / sysctl kern.maxfiles output if there's a
way to capture it before the host locks up.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗