anthropics / anthropics/claude-code
[BUG] Windows/MSIX Desktop: bundled Claude Code engine retries a failing fswatch-probe open ~38,000x/s forever, leaking ntfs.sys NtFC nonpaged pool ~230 MB/min until reboot (workaround: CLAUDE_CODE_TMPDIR)
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 146k
- Forks
- 23.8k
- PR merge metrics
- PR metrics pending
Description
### Preflight Checklist
- [x] I have searched existing issues. Closest: #45889 (same kernel signature, closed as not planned and locked; its bot comment asks for a new issue) and #94198 (open, attributes the same pool tag to CoworkVMService). This report identifies a different, specific source.
- [x] This is a single bug report.
- [ ] Latest version: this is the Claude Code engine bundled by Claude Desktop (2.1.275), not one I can update independently.
### Summary and workaround
On Windows, opening a code-mode session in MSIX-packaged Claude Desktop starts a bundled Claude Code engine that retries a failing `fswatch-probe-*` file open about 38,000 times per second, forever. Each failed open goes through the MSIX AppData redirection, and Windows leaks one ntfs.sys nonpaged pool allocation (tag `NtFC`) per failure: about 230 MB per minute, never freed until a Restart. It took a 27.8 GB machine to 0 GB available in about four hours.
**Workaround (verified):** set the user environment variable `CLAUDE_CODE_TMPDIR` to a folder outside `%LOCALAPPDATA%` (I used a folder on `D:`), then fully quit and restart Claude Desktop. Result: `NtFC` +0 allocations with two engines running, engine file operations ~10/s instead of ~100,000/s.
### What's Wrong?
While the engine process runs, the kernel nonpaged pool tag `NtFC` (ntfs.sys, file-create path) grows by 210 to 235 MB per minute, about 40,000 allocations per second of ~96 bytes each, with no frees. Earlier the same day it reached 22.6 GB (247 million outstanding allocations) in about four hours: 0 GB available, process working sets squeezed to 1.6 GB total, while the sum of all process private bytes was only 9.7 GB, so no user-mode process accounts for it.
This matches the signature in #45889, in particular the diagnosis in https://github.com/anthropics/claude-code/issues/45889#issuecomment-4768767592 (~100k file-control ops/s, ~190 MB/min, MSIX-packaged Desktop). The difference: in that report the Electron **main** process was the source. Here the Desktop shell is cleared and the source is the **spawned Claude Code engine**:
| Process | File "other" ops/s (10 s sample) |
|---|---|
| `claude.exe` engine, child of Desktop main, `%APPDATA%\Claude\claude-code\2.1.275\claude.exe --output-format stream-json ... --resume=` | **99,643** |
| MsMpEng.exe (Defender, following the opens) | 3,005 |
| Claude Desktop main process | 701 |
| Terminal Claude Code 2.1.268 (WinGet install, outside the MSIX container), actively working | 404 |
The engine did 14.1 million "other" operations and 957 reads in its first two minutes, and its session transcript was not written after the second it started, so this is not work the session was doing.
### Evidence that the engine is the trigger (start and stop, three times)
All readings are the `NtFC` row of `NtQuerySystemInformation(SystemPoolTagInformation)`, 2026-09-18, local time. `ntfs.sys` is the only file under `System32\drivers` containing the tag bytes.
1. After a Restart, Desktop not running: `NtFC` = 0 MB / 0 outstanding on every sample taken from 5 to 15 minutes after boot (Cowork service, WslService and vmcompute all running the whole time).
2. Desktop started 19:27:36, engine process started 19:28:20. Readings every 20 s: 5.7 MB (19:28:00), 53.7, 130.2, 210.6, 282.5, 363.2 MB (19:29:42); 601.7 MB at 19:30:43.
3. Engine process stopped, **Desktop left running (13 to 14 processes)**: exactly 2,399.7 MB / 26,210,628 outstanding on four consecutive readings over 62 s.
4. Session reopened, new engine started 19:36:48 resuming the same session: +30 MB within 9 s, 2,463.3 MB at 19:37:13 (2.0 M other-ops in its first 25 s).
5. Desktop quit: growth continued to the last reading with the engine up (3,456.7 MB at 19:42:07), then exactly 3,496.5 MB / 38,190,719 outstanding on five readings after exit.
### What the engine is doing (ETW Microsoft-Windows-Kernel-File, 3.05 s, 0 events lost)
Captured 15 s after the engine started, keywords CREATE + FILEIO, 509,678 events.
- The engine issued 233,500 of the 233,908 Create events in the trace (76,500/s). Desktop's main process did 298 file operations in the same window.
- Every one of the engine's opens is `fswatch-probe-<6 random chars>` in `%LOCALAPPDATA%\Temp\claude\`, disposition FILE_OPEN, a fresh random name each time (233,484 distinct names). Nothing else: no project files, no AppData tree scan.
- Each logical attempt is two kernel creates:
1. `\Users\\AppData\Local\Temp\claude\fswatch-probe-X` ends with `0x00000104 STATUS_REPARSE` (116,749 times): the MSIX redirection.
2. `\Users\\AppData\Local\Packages\Claude_pzs8sxrjxfjjc\LocalCache\Local\Temp\claude\fswatch-probe-X` ends with `0xC000003A STATUS_OBJECT_PATH_NOT_FOUND` (116,750 times). `...\LocalCache\Local\Temp` did not exist in the package location at the time (verified). Creating it does not stop the loop; see the follow-up tests.
- About 38,000 attempts per second, no backoff, no limit.
- Leak accounting: `NtFC` outstanding grew by 153,158 between two readings about 4 s apart bracketing the trace; 38,279 attempts/s x 4 s = 153,116. Consistent with **one leaked ~96-byte allocation per failed redirected open** (the readings have one-second timestamp resolution, so this is a fit, not a proof).
So there are two defects: (1) the Claude Code engine's fs.watch probe loops forever at full speed when its probe open fails, and (2) Windows leaks nonpaged pool in ntfs.sys for each open that is reparsed by the MSIX AppData redirection and then fails. (2) is Microsoft's, but (1) is what turns it into gigabytes per hour.
### Controls: the leak needs the container redirection, not just opens or failed opens
All run from a normal (non-packaged) process with Desktop closed, reading `NtFC` before and after:
| Control | Opens | Rate | `NtFC` allocations leaked |
|---|---|---|---|
| Open and close every file in the package's real data directory (14,588 files, 3 passes) | ~44,000 | ~300/s | 0 |
| Failing opens, parent folder missing (win32 error 3), NTFS path | 100,000 | 32,900/s | 0 |
| Failing opens, folder exists, file missing (win32 error 2) | 100,000 | 31,100/s | 0 |
| For comparison: the engine inside the container | ~38,000 attempts/s | | ~38,000/s |
### Follow-up tests
1. **Creating the missing folder does not help.** With `...\Packages\Claude_pzs8sxrjxfjjc\LocalCache\Local\Temp\claude` created by hand, a new engine still leaked (+186 MB in 53 s) and its file-operation rate rose from ~100,000/s to ~130,000/s; the folder stayed empty. So the engine is not failing to create the probe; it appears to be looking for something it expects to be there. (No trace taken of this run.)
2. **`CLAUDE_CODE_TMPDIR` outside AppData fixes it.** After a full Desktop restart: `NtFC` +0 allocations over 90 s and again over a further 30 s with two engines running; engine file operations ~10/s; no `fswatch-probe-*` items left behind, so the probe completed. The only difference from the failing runs is that the engine's temp path is no longer under the MSIX-redirected `%LOCALAPPDATA%`.
The string `fswatch-probe-` and the messages `[fs.watch probe] no probe dir:`, `[fs.watch probe] no probe file:`, `[fs.watch probe] verdict:` and `[settings] native file watching is not delivering events on this host (...); polling settings files every ...` are present in both bundled engines on this machine (2.1.271 and 2.1.275) and absent from terminal Claude Code 2.1.268, so the probe arrived between 2.1.268 and 2.1.271. That fits the leak first appearing on 2026-09-16, the day after engine 2.1.271 was written to disk.
### Local configuration worth knowing
On this machine `%LOCALAPPDATA%\Temp\claude` is a **junction** to a folder on another volume (ReFS Dev Drive), created 2026-09-05 to keep Claude Code's temp files off `C:`. I do not know whether the junction is required to reproduce. My first hypothesis (the junction hides the fact that the redirected folder is missing) did not survive follow-up test 1. I did not test replacing the junction with a real folder. If the junction is not required, this affects every MSIX Desktop install with the newer engine.
### Not the Cowork VM
`C:\ProgramData\Claude\Logs\cowork-service.log` records real VM boots explicitly ("Starting VM with config", Plan 9 shares, "sdk-daemon is ready"). Six boots since 2026-08-24, the last on 2026-09-07; none on either day the leak occurred. On the leak day Desktop connected to the service and only a session record was created; at shutdown the service logged "VM already stopped, nothing to release". No `vmmem`/`vmwp` process existed at any measurement. (#94198 attributes an `NtFC` leak to CoworkVMService; that reporter may be seeing this instead. I have not verified their case.)
### What Should Happen?
An idle code-mode session does negligible file I/O. A failing fs.watch probe gives up after a bounded number of attempts and falls back to polling, as its own log message describes.
### Steps to Reproduce
1. Windows 11, MSIX-packaged Claude Desktop, `CLAUDE_CODE_TMPDIR` not set. (Possibly also required: `%LOCALAPPDATA%\Temp\claude` being a junction; see above.) Restart the machine.
2. Read the `NtFC` pool tag (poolmon `-b`, PoolMonX, or `NtQuerySystemInformation` class 22). A coarse substitute: `(Get-CimInstance Win32_PerfFormattedData_PerfOS_Memory).PoolNonpagedBytes`.
3. Start Claude Desktop and open (or resume) a code-mode session. Do nothing in it.
4. Read the tag every 20 s: it climbs ~70 to 80 MB per reading.
5. Stop only the engine process (`claude.exe` whose path is under `...\Claude\claude-code\\`), leaving Desktop running: growth stops at once. Reopen the session: growth resumes within seconds.
### Suggested fixes
In order of value: bound the probe's retries (count and backoff) so a failing probe costs milliseconds, not the machine; have Desktop pass the engine a temp path outside the package-redirected AppData; report the ntfs.sys leak to Microsoft.
### Not established
- Whether the junction is required (above).
- What the probe does at the JavaScript level and why it opens (FILE_OPEN) a fresh random name each time; I have only the kernel's view.
- Whether every code-mode session does this or only the one observed. The probe path does not depend on the project, so probably every session on this machine.
- The "clean uninstall and reinstall fixes it" workaround from #45889: not tried.
### Is this a regression?
Probably. The probe strings are absent from 2.1.268 and present in 2.1.271 and 2.1.275; the leak was first noticed 2026-09-16.
### Environment
- Windows 11 Enterprise 25H2, build 26200.9106, 27.8 GB RAM. `C:` NTFS, `D:` ReFS (Dev Drive).
- Claude Desktop MSIX package `Claude_2.2553.1.0_x64__pzs8sxrjxfjjc`.
- Bundled engine `claude-code\2.1.275` (written to disk 2026-09-18 14:09 local; `2.1.271` also present, written 2026-09-15).
- Terminal Claude Code 2.1.268 via WinGet on the same machine: does not move the tag.
A Restart (not Shut down, per #94198; not verified here) reclaims pool already leaked. I have the 3-second kernel file trace and can share it privately.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the bundled Claude Code engine's fs.watch probe and the handling of the fswatch-probe-* opens under the MSIX-redirected temp path. Reproduce with the listed Windows 11/MSIX steps, compare behavior with CLAUDE_CODE_TMPDIR set, and verify that a failing probe stops retrying rapidly and falls back to polling without continued NtFC growth.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript
- Domain
- desktop-dev, operating-systems, tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100