[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)

Status Open
Reported on v2.1.275
Maintainer reply None cached
Activity 4 comments · opened Sep 19, 2026

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=<session> | 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\<user>\AppData\Local\Temp\claude\fswatch-probe-X ends with 0x00000104 STATUS_REPARSE (116,749 times): the MSIX redirection.
  2. \Users\<user>\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\<version>\), 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.

View original on GitHub ↗

3 Comments

jallers · 26 days ago

Independent confirmation, and a correction to my "Not established" list. @Ghostgfdsrhj reported the same root cause a few hours before I filed this, in #94198 (https://github.com/anthropics/claude-code/issues/94198#issuecomment-5734974409 and https://github.com/anthropics/claude-code/issues/94198#issuecomment-5735671566): same Desktop 2.2553.1.0 and bundled 2.1.275, Process Monitor showing fswatch-probe-* opens REPARSE then PATH NOT FOUND, 0.334 leaked allocations per operation, on a machine with no WSL and an idle Cowork service. I had not read those comments when I filed; the first diagnosis is theirs. Their report does not mention a junction at %LOCALAPPDATA%\Temp\claude (I have asked), so the junction on my machine is probably not required, and this likely affects MSIX Desktop installs with this engine generally. What this issue adds: the verified CLAUDE_CODE_TMPDIR workaround, the folder-creation test that does not help, the outside-the-container controls, and the 2.1.268 to 2.1.271 version window.

Ghostgfdsrhj · 26 days ago

Same machine as my two comments on #94198, and an answer to your question there: no, %LOCALAPPDATA%\Temp\claude is not a plain folder here either. It is a junction too, to a folder on an NTFS volume (D:), not ReFS. So ReFS is not part of the repro, but the junction question stays open: both machines that reproduce have one. CLAUDE_CODE_TMPDIR fixes it here too.

Setup

  • Windows 11 Home 25H2, build 26200.9457, KB5129195 installed
  • Claude Desktop 2.2553.1.0 (MSIX). Bundled engines present: 2.1.274 and 2.1.275; both binaries contain the strings fswatch-probe and CLAUDE_CODE_TMPDIR (checked with findstr)
  • %LOCALAPPDATA%\Temp\claude is a junction to a folder on D:, which is NTFS, not ReFS
  • %LOCALAPPDATA%\Packages\Claude_pzs8sxrjxfjjc\LocalCache\Local\Temp does not exist
  • Minifilters loaded: WdFilter, bindflt, wcifs, UnionFS, CldFlt. No third-party AV. cowork-svc idle, no vmmem/vmwp processes

Before the workaround

  • Pool tag NtFC (ntfs.sys): 56.5M allocations vs 119k frees, ~96 bytes each, growing at 85,000 to 127,000 allocations/s (~10 MB/s) with two engine sessions open. 13.5 GB after 17 minutes, 18.6 GB within 48 minutes of a fresh boot
  • Each engine process (claude-code\2.1.275\claude.exe, spawned by the Desktop app): 100% of one core, ~161,000 "IO other" operations/s, near-zero reads/writes, handle count stable at ~275
  • Nonpaged charge of all claude.exe processes combined: 0.5 MB, so not a user-mode leak
  • Control: the same kinds of operations from a normal, non-packaged PowerShell process (300k File.Exists on existing, missing and unique-name paths, 300k opens, 20k unique temp-file creates with delete-on-close) added 0 NtFC allocations. Only the packaged process leaks
  • The Process Monitor capture in https://github.com/anthropics/claude-code/issues/94198#issuecomment-5735671566 is from this machine: 30,231 distinct fswatch-probe-* names in 1.99 s, each REPARSE then PATH NOT FOUND, 0.334 leaked allocations per operation. The numbers here are a separate run, read via NtQuerySystemInformation(SystemPoolTagInformation) (the same source poolmon uses), and they fit that capture

After the workaround

  • User env var CLAUDE_CODE_TMPDIR=D:\<folder> (outside %LOCALAPPDATA%), then a Windows restart
  • Two engines (2.1.275) running: NtFC 0 MB, 0 live allocations, +4 allocations in 40 s. Engine processes at 0.3% to 0.9% CPU and 11 to 17 IO ops/s. Nonpaged pool 1.16 GB, moving -1.8 MB over 30 s
  • The engine now builds its temp tree under <CLAUDE_CODE_TMPDIR>\claude\. No fswatch-probe-* leftovers in either location

Not tested here: replacing the junction with a plain folder. A plain-folder install of 2.1.275 that leaks would close that question.

peterlau03 · 20 days ago

Same tag and the same MSIX AppData redirect, but broader: on MSIX Claude Desktop, any process a session spawns leaks NtFC on every open through a junction or symlink under %APPDATA%/%LOCALAPPDATA%. uv Python, nvm and pnpm globals all hit it, with no retry loop needed.

A WPR stack trace puts the root cause in bindflt.sys: its pre-create probe drops the reparse buffer on STATUS_STOPPED_ON_SYMLINK. It is reported to Microsoft as https://aka.ms/AA13ld3q, and the details, a one-minute repro and workarounds that measured 0 are in #96870. The fswatch-probe case here may well be the same bindflt bug class, reached through the redirect rather than a junction, but I have not traced that one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Showing cached comments. Read the full discussion on GitHub ↗