[BUG] Windows: idle session spins at 100% CPU after a watched ~/.claude directory is permanently deleted (libuv fs.watch bug; fix already merged upstream)
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
What's Wrong?
On Windows, an idle Claude Code session spins at ~100% of one core indefinitely. Root-caused it with a debugger-grade forensic pass: it's the known libuv fs.watch bug where permanently deleting a watched directory sends the ReadDirectoryChangesW loop into an infinite error-retry spin — and Claude Code both watches and permanently deletes directories under ~/.claude, so it can trigger the bug against itself.
Evidence (captured live on the spinning process, v2.1.211, Windows 11 10.0.26200, NTFS):
- Idle interactive session pinned at ~116% of one core; main thread accumulated 1h27m CPU.
- ~500,000 "Other" I/O operations/second with zero read/write operations — the signature of a
ReadDirectoryChangesWfail/retry loop on a dead handle, not real work. - Sysinternals
handleshows the process holding an open handle toC:\$Extend\$Deleted\<FRN>— a watched directory that was POSIX-deleted while the watcher held its handle. NTFS USN journal dates the deletion and places the directory as a direct child ofC:\Users\<user>\.claude\. - The process also holds watch handles on
~/.claudeand~/.claude\tasks\<uuid>. Claude Code deletes completed task dirs itself, so any session can strand its own watcher. - Identical signature confirmed independently on the same machine in VS Code's fileWatcher utility process (also Node/libuv), stranded by the same deletion — see microsoft/vscode#287800.
- Healthy idle sessions on the same machine measure 0–0.2% of a core; a stranded one never recovers until the process exits. Sessions resume cleanly afterward (
claude --resume).
Root cause and fix status
- Upstream Node.js issue: nodejs/node#61398 (
fs.watch: endless events; confirmed trigger is permanent deletion — Recycle Bin moves don't trigger it). - Fix already merged in libuv master on 2026-03-31: libuv/libuv#5013 (
44125af6, "win: fix watch loop logic") — but it is in no libuv release yet (latest v1.52.1 predates it) and not cherry-picked into Node'sdeps/uv.
Since Claude Code bundles its own Node runtime, you don't have to wait for the libuv → Node → release chain: cherry-picking libuv/libuv@44125af6 into the bundled runtime fixes it immediately.
Workarounds until then
- Avoid permanently deleting watched dirs (in Claude Code's own case: task-dir cleanup under
~/.claude/tasksis a self-trigger). - Detect the spin signature (>0.8 core AND >50k other-ops/s AND ~0 read/write ops) and restart the session;
claude --resumerestores it.
Possibly related (macOS reports, cause there unconfirmed): #75630, #17148
What Should Happen?
An idle session should stay near 0% CPU; deleting a watched directory should not put the file watcher into an infinite syscall loop.