[BUG] macOS: fseventsd runaway on agent-heavy Macs (24 GB, 800x its budget, a core pinned for days, no self-recovery) — Claude Code can block on it at startup (#96008) and nothing detects it
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet (related, closed: #96008, #72394; see below)
- [x] This is a single bug report
- [x] I am using the latest version of Claude Code
What's Wrong?
On a Mac used for heavy Claude Code work, Claude Code kept running for days on top of a crippled fseventsd: 24 GB against Apple's own 30 MB budget (launchctl print system/com.apple.fseventsd | grep 'jetsam memory limit (active': jetsam memory limit (active, soft) = 30 MB), one core pinned for about two days after the burst of file activity was over, swap at 13,548 of 14,336 MB. Claude Code neither detects this state nor, per the last comment on #96008, survives it gracefully: watcher registration on the main thread can block startup for a minute. The daemon did not recover by itself over the 46 hours I could see; a restart (sudo kill -TERM) reset it.
From Apple's diagnostic reports and the daemon's own log IDs:
| | boot → 28 Sep 10:15 | 28 Sep 10:15 → 30 Sep 01:39 | 30 Sep 01:39 → 1 Oct 23:00 | 1 Oct 23:20 |
|---|---|---|---|---|
| file events per second, daily average | 50–200 (14–21 Sep), rising to 4,694 (27 Sep) | 5,715 (28 Sep), 700 (29 Sep) | 1,107 (30 Sep) | 195 (30-second measurement) |
| fseventsd share of one core | 1.5 % | 16 % average (63 % in a macOS report at 28 Sep 23:20) | 97–98 % | pinned |
| fseventsd memory | 10–42 MB (27 Sep → 28 Sep 07:59) | 0.04 GB (28 Sep 10:15) → 1.7 GB (28 Sep 23:20) → 7.93 GB (30 Sep 01:39) | 11.17 GB (30 Sep 06:27) | 24 GB |
The load fell eightfold after 28 Sep; the daemon's CPU use went up. Root sample of the daemon: its disk-log thread spent 93 % of samples in _platform_strncmp; the delivery thread waited on a mutex about half the time. Only 176 Mach ports, so this is accumulated state, not many live clients. launchctl kickstart -k is refused by SIP (error 150); sudo kill -TERM $(pgrep -x fseventsd) worked and launchd respawned it: 7.4 MB, under 1 % CPU, swap down to 1.5 GB.
Full write-up with every number, the methods and the scripts: https://github.com/talkstream/fseventsd-runaway
Why this is a Claude Code issue, and what I am not claiming:
- #96008 was closed by its own reporter on 23 Sep after he found the trigger; its last comment names "the remaining Claude Code bug: FSEvents watchers (skills, commands, settings) are registered in a way that blocks the main thread, so a slow fseventsd freezes all input at startup" (2.1.280). That part was never picked up. This report is about it, with the daemon-side data behind it.
- The team already knows the state exists: CHANGELOG 2.1.271, "Fixed settings file changes made outside the session going unnoticed on macOS machines whose system file-event service is saturated; the watcher now falls back to polling". Users are still not told, and the machine stays crippled.
- Claude Code is an FSEvents client: the docs say "Claude Code watches
~/.claude/agents/and.claude/agents/", #19566 describes "many FSEvents watchers (for config files, skill directories, settings, etc.)", and inlsmp -p <fseventsd pid>on my Mac the two CLI session processes each held two send rights to fseventsd, while theclaude daemonprocess and its twobg-pty-hosthelpers held none. - The 2.1.286 binary references
FSEventStreamCreate,Start,Stop,Invalidate,ReleaseandScheduleWithRunLoop; the stringFSEventStreamSetExclusionPathsdoes not appear in it (strings -a … | grep FSEventStream). - I am not claiming Claude Code caused the leak. During the flood (22–28 Sep) the machine ran 8,900 to 19,500 Claude Code tool calls a day plus mutation testing and Docker via Colima; daily tool calls correlate with daily log-file counts at only r = 0.71. The leak itself looks OS-side, and I am reporting it to Apple. Compare daintreehq/daintree#12455: a 36 GB fseventsd with about 12
claudeCLIs running; the author's hypothesis is per-client state leaking under register/unregister churn, marked "not proven".
What Should Happen?
- Detect it. On macOS,
/doctorand a cheap check at session start compare fseventsd's footprint with its launchd jetsam budget and warn when it is far over, with the fix command. A working, sub-second, no-sudo implementation is in the linked repo as a SessionStart plugin (plugin/fseventsd-health, 8 selftests, about 0.3 s); I am happy to upstream it into/doctororplugins/if you want it. - Never block the main thread on
FSEventStreamStart(the open remainder of #96008; the same stack appears in manaflow-ai/cmux#12313 on macOS 27.0): start watchers asynchronously with a timeout, so a slow fseventsd degrades file watching instead of freezing input. - Reduce churn. Keep one long-lived stream per process instead of rebuilding it when a watched path is added (Node's libuv rebuilds the stream on every change; I could not check what the Bun build does), exclude hot directories with
FSEventStreamSetExclusionPaths(max 8), and skip watchers in processes that do not need live reload, such asclaude -pand subagent processes.
Error Messages/Logs
$ launchctl print system/com.apple.fseventsd | grep 'jetsam memory limit (active'
jetsam memory limit (active, soft) = 30 MB
$ sudo footprint <pid>
fseventsd [551]: 64-bit Footprint: 24 GB (16384 bytes per page)
24 GB 0 B 0 B 8162 Malloc Small
$ sudo launchctl kickstart -k system/com.apple.fseventsd
Could not kickstart service "com.apple.fseventsd": 150: Operation not permitted while System Integrity Protection is engaged
macOS cpu_resource report, 28 Sep 23:20: "90 seconds cpu time over 143 seconds (63% cpu average), exceeding limit of 50% cpu over 180 seconds" / "Action taken: none"
sample (root), disk-log thread: 2351 of 2526 samples in _platform_strncmp
Steps to Reproduce
Not deterministic; it builds up over days. To observe it:
- On macOS, run agent-heavy Claude Code work for several days (many parallel sessions and subagents, test runners, mutation testing, containers with the home directory mounted).
- Check daily:
top -l 1 -stats pid,mem,cmprs,ports -pid $(pgrep -x fseventsd)and compare withlaunchctl print system/com.apple.fseventsd | grep 'jetsam memory limit (active'. - Once it is far over budget, note that it keeps growing and keeps a core busy even after the file-event rate falls back, and that Claude Code shows no warning.
sudo kill -TERM $(pgrep -x fseventsd)resets it (savesudo ls -laT /System/Volumes/Data/.fseventsdfirst: the restart erases the event history).
Claude Model
Not sure / Multiple models
Is this a regression?
I don't know
Claude Code Version
2.1.286 (Claude Code)
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Terminal.app (macOS)
Additional Information
MacBook Pro 14" M3 Pro (Mac15,6), 18 GB, macOS 27.0 (26A428), uptime 20 days at diagnosis. Related: #96008, #72394, #19566, #66020; daintreehq/daintree#12455; abiosoft/colima#1569; manaflow-ai/cmux#12313.
Showing cached comments. Read the full discussion on GitHub ↗
3 Comments
I tried to open a pull request with the detection plugin from point 1, but pull-request creation on this repository is limited to collaborators (
createPullRequestis refused for outside accounts). The change is ready on a fork branch instead:https://github.com/anthropics/claude-code/compare/main...talkstream:claude-code:plugins/fseventsd-health
It adds
plugins/fseventsd-health/(plugin.json, hooks.json, handler, README, selftest), one entry in.claude-plugin/marketplace.jsonand one row inplugins/README.md. Read-only SessionStart hook, macOS only, about 0.3 s, silent while fseventsd is healthy;bash plugins/fseventsd-health/tests/selftest.shpasses 8/8 and fails underSELFTEST_BREAK=1. Happy to adapt it, or for you to fold the check into/doctor.A data point from two agent-heavy Macs, which may help separate "many Claude processes" from "watcher churn" as the trigger.
Setup: MacBook Pro (Mac14,10, 16 GB, macOS 15.7.3, Claude Code 2.1.285, uptime 5 days) and Mac mini (Mac16,10, 16 GB, macOS 26.3, uptime 5 days). Measured 2026-10-01 with the commands from the report.
fseventsd was healthy on both, against the same 30 MB budget:
| | footprint | CPU | Mach ports | process age |
|---|---|---|---|---|
| MacBook Pro | 6.6 MB | 0.0 % | 594 | 5 d |
| Mac mini | 6.3 MB | 0.0 % | 285 | 5 d |
On the MacBook this was despite a pile-up of headless Claude Code processes. A third-party plugin, pinned at an old version, started a headless
claude --output-format stream-jsonprocess per session and never ended it. 31 had accumulated over ~29 hours, holding 3.6 GB RSS, mostly idle. The plugin had been fixed upstream long ago; plugins from third-party marketplaces don't auto-update by default, so it sat on the leaking version for months. Updating it removed the leak.What this suggests (inferred, not proven):
--output-format stream-json/ SDK processes like these have no use for settings, skills or agents live-reload watchers. If Claude Code skipped FSEvents registration in headless/SDK mode, a leaking plugin or orchestrator couldn't multiply watcher clients at all./doctorwarns on fseventsd, it could also count long-lived headlessclaudeprocesses and flag stale plugin versions. A leaked-process pile-up is easy to miss, and it is the scenario that turns into churn.Caveat: we couldn't confirm whether those headless processes held fseventsd send rights (
lsmpwithout root returned none; inconclusive).@ayodm thank you, that is a useful control. A follow-up from my side, with one correction to how I would read my own data.
A fresh daemon stayed healthy through a heavy agent run, but that run was not a file-event flood. After the 1 Oct restart I updated to macOS 27.0.1 (26A434) and rebooted. Over the next 42 hours the hourly watch log shows 16 checks, all within budget at 4.6–5.5 MB. That window included one session with eight parallel agents; between 19:17 and 21:40 the session fetched 61 short videos and software-decoded at least 58 of them; most likely the machine got hot from that CPU load. Read as root, the event log for that window shows 22–190 events/s per hour. That is an ordinary rate; the flood day (28 Sep) averaged 5,715/s. So my run says nothing about churn at flood scale, only that heavy CPU work at normal event rates does not trigger it.
Taken together: 31 mostly idle headless processes (your MacBook; whether they held fseventsd send rights is open, as you note) and heavy CPU work at ordinary event rates (mine) both left the daemon at about 5–7 MB. On my machine the 24 GB state came after days of flood (22,067 log files on the peak day against 95–600 on normal days) and then did not come back down. One caveat: my "before" and "after" are on different builds (27.0 vs 27.0.1).
On your
lsmpcaveat: without root it fails withtask_for_pid() failed. As root,sudo lsmp -p $(pgrep -x fseventsd)works. Mine held 176 Mach ports at 24 GB, and 211 at 5 MB on 3 Oct, against your 594 and 285 at about 6 MB. Port count does not track the growth, which also argues for accumulated internal state rather than live clients.+1 to skipping FSEvents registration in headless/SDK mode, and to
/doctorcounting long-lived headless processes. Both shrink the churn surface without needing to know the leak mechanism.Write-up updated: https://github.com/talkstream/fseventsd-runaway#11-follow-up-23-oct-hot-again-and-it-was-not-fseventsd