[BUG] Backend busy-polls at ~266k syscalls/sec with zero file I/O, holding 1.1 cores per process indefinitely (Windows, 2.1.234)
Summary
Claude Code backend processes enter a permanent busy-poll and hold ~1.1 CPU cores each, indefinitely, on Windows. This is measured rather than inferred: the spinning processes perform zero file I/O while issuing ~266,000 non-file I/O operations per second each.
Related but closed without a fix: #11473 (closed "not planned"), #22275 (closed as duplicate), #19393, #30807, #17148. Filing fresh because those are stale, none has Windows syscall-level numbers, and none includes a control group.
Environment
| | |
|---|---|
| Claude Code | 2.1.234 |
| OS | Windows 11 Home 26100 |
| CPU | AMD Ryzen 7 7800X3D, 8C/16T |
| RAM | 94.9 GB |
| Shape | One app instance (1 window) hosting ~40-56 claude.exe backends |
Measurement
Four spinning backends, sampled over 5 seconds each via Win32_Process counters:
pid 18500 cpu+ 6.2s fileIO+0 otherIO+1,359,847 threads 35 handles 361
pid 14796 cpu+ 5.7s fileIO+0 otherIO+1,333,536 threads 35 handles 349
pid 10656 cpu+5.62s fileIO+0 otherIO+1,304,113 threads 36 handles 390
pid 22876 cpu+5.45s fileIO+0 otherIO+1,275,591 threads 35 handles 422
ReadOperationCount + WriteOperationCount delta is exactly zero. OtherOperationCount delta is ~1.3M in 5s, i.e. ~266,000 syscalls per second per process. CPU delta of 5.7s over 5s wall = ~114% of a core.
Nothing is being read or written. That is a wait syscall returning immediately instead of blocking, which matches the recvfrom() -> EAGAIN -> retry trace in #11473 exactly, on a different OS.
Thread and handle counts are stable across samples, so this is not a leak.
What it costs
With 13 backends spinning, the whole machine sat at a flat 100%:
8 one-second samples: 100, 100, 100, 100, 100, 100, 100, 100
min 100 max 100 avg 100
Two unrelated 9-minute build jobs then overran a 10-minute ceiling. \Processor Information(_Total)\% Processor Utility read 109.5%, so the CPU was turboing above base: this is not thermal throttling.
Worth noting for anyone triaging: Task Manager under-reports this badly (61% against a true 100%), because at saturation it cannot get scheduled often enough to sample itself. Performance-counter readers are accurate; Task Manager is not. That may be why some reports of this get dismissed.
Ruled out
- Not orphaned processes. All four spinners had live parents, all sharing one app instance.
- Not user hooks or file watchers. This project runs heavy
SessionStart/PreToolUsehooks; a hook scanning the tree would movefileIO, andfileIOis flat zero throughout. - Not thermal throttling. See the 109.5% utility figure above.
- Not a handle or thread leak. Both counts stable.
A correlation worth investigating, and a negative result
Every spinning backend had exactly one notable child:
cmd.exe /d /s /c "npx -y @modelcontextprotocol/server-pdf --stdio"
That is the bundled pdf-viewer plugin's MCP server, communicating over a stdio pipe. A stdio pipe poll is consistent with the syscall signature.
Control group, sampled over 5s across all claude.exe:
| | count | avg | notes |
|---|---|---|---|
| With the server-pdf child | 34 | 25% of a core | 8 of them at ~100% |
| Without it | 22 | 2% of a core | max 31%, none spinning |
So the pdf MCP child appears necessary but not sufficient: nothing without it spins, but only 8 of 34 with it do. During this session the pdf MCP server was observed disconnecting and reconnecting, which may be the trigger.
However, killing it does not stop an already-spinning process:
BEFORE: pid 18500 at 97% of a core
killed the server-pdf child and its 2 grandchildren
AFTER: pid 18500 at 86% of a core (control pid 22876, child still alive: also 86%)
So once started the loop is self-sustaining and survives removal of the suspected trigger. I am deliberately not claiming pdf-viewer as the cause. Identifying the actual polled handle needs an ETW trace.
A second, separate problem
The same bundled plugin had spawned 100 concurrent server-pdf processes holding 6.77 GB (avg 69 MB each), on a machine where nothing was using PDF tooling:
server-pdf processes: 100
RAM held: 6.77 GB
total claude.exe: 56
There appears to be no supported way to disable it: it is not in installed_plugins.json, not in enabledPlugins, and claude plugin list does not show it. A bundled plugin that spawns one npx process per session with no opt-out is worth a look independently of the CPU bug.
What would close this out
An ETW/WPR trace on a spinning process would name the exact call. I did not have Windows Performance Recorder available. If a maintainer can say which handle the event loop is expected to block on, that is probably enough to spot the regression by inspection.
Reproduction
Not reliably reproducible on demand. It appears on a long-lived box running many concurrent sessions under one app instance, and once a backend starts spinning it never recovers: only ending that session frees the core.