Bash tool's bundled `find` (bfs) keeps one fd per directory up to the process fd limit; a `find ~` walk exhausted the macOS vnode table and caused a kernel panic

Status Open
Reported on v2.1.296
Maintainer reply None cached
Activity 0 comments · opened Oct 10, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report
  • [x] I am using the latest version of Claude Code (measured on 2.1.296)

What's Wrong?

On macOS, the Bash tool's shell snapshot replaces find with a function that runs the bundled bfs 4.1.1 (the snapshot block "Shadow find/grep with embedded bfs/ugrep"). In 2.1.295 the function is:

ARGV0=bfs "$_cc_bin" -S dfs -regextype findutils-default "$@"

The hard RLIMIT_NOFILE is unlimited (the macOS default), so the per-process cap is kern.maxfilesperproc = 245,760. bfs sizes its cache of open directory handles from that limit, so one walk of a large tree keeps thousands of directories open at once. A soft limit does not help, because bfs raises its own soft limit to the hard limit. Each open handle pins a vnode, and the system-wide vnode table holds 263,168 entries (kern.maxvnodes).

On our Mac (Mac14,14, macOS 26.5.1 25F80, 128 GB), an agent in a Claude Code 2.1.293 session ran:

find ~ -maxdepth 5 -path '*skills/foo/SKILL.md' -newer ~/.claude/skills/foo/SKILL.md

Free vnodes were already low from other load (1,038 free three minutes earlier). Seven seconds after the walk started, the kernel panicked:

panic(cpu 9 caller …): initproc exited -- exit reason namespace 2 subcode 0xa description: none
Panicked task …: pid 1: launchd

launchd took SIGBUS inside dyld4::APIs::dladdr → dyld3::MachOLoaded::findClosestSymbol. In the panic stackshot, all 8 bfs threads and 18 threads of other processes (node, fseventsd, syspolicyd, Finder, bash, sleep and others) were blocked in APFS on the same kernel wait. A second panic, a kernelmanagerd watchdog stall, followed during the recovery boot.

The same walk raised TCC prompts attributed to "Claude": Documents, Desktop, Downloads, Photos, Calendar, Contacts, then "access data from other apps". bfs runs as the claude binary, so macOS attributes the walk to Claude.

We have not found what drained the vnode table before the walk. The walk was the trigger, not the whole cause. But the bundled find is the only tool we measured that pins vnodes at this scale, and agents run find ~ often.

What Should Happen?

The bundled find should hold a bounded number of directory handles, close to what /usr/bin/find holds. One walk of ~ should not be able to pin tens of thousands of vnodes.

A possible fix is to run the bundled bfs under a fixed hard fd limit. Measured on 2.1.296, with both soft and hard limits set: a limit of 1,024 caps bfs at 1,028 handles, and a limit of 64 caps it at 68. The sorted output is identical to an unlimited run and to /usr/bin/find. Note that ulimit -Hn N alone fails while the soft limit is above N, so the soft limit has to be lowered first.

Error Messages/Logs

panic(cpu 9 caller 0xfffffe003aee00b0):  initproc exited -- exit reason namespace 2 subcode 0xa description: none

/Library/Logs/DiagnosticReports/*.diag reports from the same afternoon show Vnodes Available: 0.03% to 0.55%.

Steps to Reproduce

This measures handles and vnodes only. It does not panic the machine.

  1. On macOS, in a Claude Code session, have the Bash tool run type find. It prints "find is a shell function from …/shell-snapshots/…".
  2. Pick a tree with tens of thousands of directories within depth 4. Ours has about 116,000. Have the Bash tool run:

``sh
find ~/projects -maxdepth 5 >/dev/null 2>&1 & p=$!
mx=0; for i in $(seq 1 100); do for c in $p $(pgrep -P $p); do n=$(lsof -p $c 2>/dev/null | wc -l); [ $n -gt $mx ] && mx=$n; done; sleep 0.05; done; wait; echo "peak fds: $mx"
``

  1. Repeat with /usr/bin/find, and with (ulimit -Sn 64; ulimit -Hn 64; find …).

Our results on 2.1.296. We invoked the 2.1.296 binary the same way as the 2.1.295 function above. Each walk was ~/projects -maxdepth 5:

| Walker | Peak open handles | Drop in kern.free_vnodes during the walk |
|---|---|---|
| bundled bfs, default limits (2 runs) | 5,683 / 5,044 | 34,849 / 27,406 |
| /usr/bin/find (2 runs) | 10 / 11 | none |
| bundled bfs, soft limit 256 only | 4,847 | not measured |
| bundled bfs, soft+hard limit 1,024 | 1,028 | 9,516 |
| bundled bfs, soft+hard limit 64 | 68 | 10,699 |

The capped runs still lowered free vnodes by about 10,000, against none for /usr/bin/find. We could not separate that from background activity on the machine.

Is this a regression?

I don't know.

Claude Code Version

2.1.293 at the panic (the desktop app's bundled CLI). 2.1.295 and 2.1.296 for the measurements.

Platform / Operating System / Terminal

Anthropic API / macOS 26.5.1 / Claude desktop app (Code tab).

Related

  • #79920: same panic signature; there, a background-session daemon exhausted the file table.
  • #35556: same panic signature with vnode exhaustion. It was closed as not Claude Code. This report is specific to the Claude Code Bash tool.

View original on GitHub ↗