Current Behavior
The Nx daemon starts a workspace-wide output-tracking watcher in addition to its source watcher. On Windows, both use non-recursive per-directory registrations.
This duplicates resource cost for normal source directories and extends watcher cost into Git-ignored directories:
| Directory type |
Source watcher |
Output watcher |
Current HANDLE cost |
| Normal source directory |
Yes |
Yes |
4 per directory |
| Git-ignored directory |
No |
Yes |
2 per directory |
Hard-excluded (node_modules, .git, .nx/cache, .nx/workspace-data) |
No |
No |
0 |
The public fixture is Git-ignored specifically to isolate the output watcher's two-HANDLE slope. The defect itself is workspace-wide duplication, not Git-ignore handling.
Nx uses notify 8.2.0. For every Windows directory registration, notify retains:
- a directory HANDLE created by
CreateFileW, and
- a completion-semaphore HANDLE created by
CreateSemaphoreW.
A HANDLE is a Windows kernel resource owned by the process. The count therefore grows with workspace directories and remains allocated for the daemon lifetime.
The public synthetic reproduction proves the slope on native Windows:
| Generated directories |
Daemon HANDLEs |
Private memory (MB) |
Working set (MB) |
Graph command |
| 0 |
288 |
96.6 |
91.1 |
2,664ms |
| 8,192 |
16,674 |
236.3 |
230.4 |
3,912ms |
| 32,768 |
65,826 |
644.4 |
639.7 |
8,110ms |
| 131,072 |
262,434 |
2,311.2 |
2,303.7 |
26,146ms |
Across all four points:
- HANDLE slope: 2.000008 per directory, R² effectively 1.0.
- Private-memory slope: 0.0169 MB per directory (~16.9 KB), R² 0.99999.
- Graph-command slope: 0.180ms per directory, R² 0.99969.
- The measured daemon PID exited after reset at all four points.
A separate large-workspace field observation reached 161,876 handles and approximately 2.48 GB. It motivated the investigation; the public synthetic result is the independently reproducible proof.
Expected Behavior
Optional output tracking should not add a second workspace-wide per-directory watcher on Windows. Nx must preserve cache correctness through its existing conservative fallback while leaving source watching unchanged.
After the fix, normal source directories remain watched once by the source watcher, while Git-ignored directories no longer incur output-watcher registrations.
GitHub Repo
https://github.com/sdjayna/nx-daemon-reproductions
Successful native Windows evidence run:
https://github.com/sdjayna/nx-daemon-reproductions/actions/runs/30946807413
Steps to Reproduce
- On Windows, install the reproduction dependencies.
- Run
./scripts/measure-windows-handles.ps1 -DirectoryCount 1000.
- Record daemon PID, HANDLE count and memory.
- Run
./scripts/measure-windows-handles.ps1 -DirectoryCount 5000.
- Compare the two output blocks. The script resets the daemon before each measurement.
- Run
corepack yarn nx reset afterwards and confirm the measured PID exits.
Nx Report
Node : 24.18.0
OS : win32-x64
Native Target : x86_64-windows
yarn : 4.15.0
daemon : Available
nx : 23.1.1
Failure Logs
There is no thrown application error. The failure is linear process-resource retention; the workflow emits the process metrics above.
Package Manager Version
Yarn 4.15.0. The watcher mechanism itself is package-manager independent.
Operating System
Additional Information
Draft PR #36566 fixes this scoped output-watcher defect:
#36566
The PR disables the optional output watcher on Windows and uses Nx's existing conservative output-cache fallback. Source watching remains unchanged and is explicitly outside this issue's scope.
A future source-watcher redesign must be tracked separately and must not silently lose changes. notify 8.2.0 lacks the rescan behavior required for a safe recursive-root change; current notify main has added that support, but Nx does not yet consume a released version containing it.
Current Behavior
The Nx daemon starts a workspace-wide output-tracking watcher in addition to its source watcher. On Windows, both use non-recursive per-directory registrations.
This duplicates resource cost for normal source directories and extends watcher cost into Git-ignored directories:
node_modules,.git,.nx/cache,.nx/workspace-data)The public fixture is Git-ignored specifically to isolate the output watcher's two-HANDLE slope. The defect itself is workspace-wide duplication, not Git-ignore handling.
Nx uses
notify8.2.0. For every Windows directory registration,notifyretains:CreateFileW, andCreateSemaphoreW.A HANDLE is a Windows kernel resource owned by the process. The count therefore grows with workspace directories and remains allocated for the daemon lifetime.
The public synthetic reproduction proves the slope on native Windows:
Across all four points:
A separate large-workspace field observation reached 161,876 handles and approximately 2.48 GB. It motivated the investigation; the public synthetic result is the independently reproducible proof.
Expected Behavior
Optional output tracking should not add a second workspace-wide per-directory watcher on Windows. Nx must preserve cache correctness through its existing conservative fallback while leaving source watching unchanged.
After the fix, normal source directories remain watched once by the source watcher, while Git-ignored directories no longer incur output-watcher registrations.
GitHub Repo
https://github.com/sdjayna/nx-daemon-reproductions
Successful native Windows evidence run:
https://github.com/sdjayna/nx-daemon-reproductions/actions/runs/30946807413
Steps to Reproduce
./scripts/measure-windows-handles.ps1 -DirectoryCount 1000../scripts/measure-windows-handles.ps1 -DirectoryCount 5000.corepack yarn nx resetafterwards and confirm the measured PID exits.Nx Report
Failure Logs
There is no thrown application error. The failure is linear process-resource retention; the workflow emits the process metrics above.
Package Manager Version
Yarn 4.15.0. The watcher mechanism itself is package-manager independent.
Operating System
Additional Information
Draft PR #36566 fixes this scoped output-watcher defect:
#36566
The PR disables the optional output watcher on Windows and uses Nx's existing conservative output-cache fallback. Source watching remains unchanged and is explicitly outside this issue's scope.
A future source-watcher redesign must be tracked separately and must not silently lose changes.
notify8.2.0 lacks the rescan behavior required for a safe recursive-root change; currentnotifymain has added that support, but Nx does not yet consume a released version containing it.