Command
serve
Is this a regression?
The previous version in which this bug was not present was
21.2.24 (watchpack)
Description
ParcelExternalManager.ensureDirWatched (packages/angular/build/src/utils/watcher.ts) calls parcelWatcher.subscribe() once per directory outside the workspace root. On macOS every subscription is its own FSEvents stream, and macOS refuses to start more than about 512 streams per process (parcel-bundler/watcher#190). The rejection is caught and ignored ("Ignore subscription errors for missing or restricted external directories"), so the files in those directories are silently not watched and editing them never triggers a rebuild.
Our setup: npm workspaces monorepo; the Angular workspace (angular.json) lives in <monorepo>/apps/<APP> and the app imports ~100 libraries from <monorepo>/libs/* through tsconfig paths. One ng serve registers 1,278 external directories (about 1,000 library source folders plus 274 node_modules packages). Instrumenting the catch in ensureDirWatched on 22.2.1 (which already contains #34187):
- 1,278 subscribe attempts
- 511 succeed
- 767 fail with
Error starting FSEvents stream
Which directories end up watched depends on subscription order. I edited 22 files in one batch with ng serve --verbose: 1 app file, 11 library files in directories whose subscription succeeded, and 10 in directories whose subscription failed. The 12 in watched directories show up in the modified list and rebuild; the 10 others never do (waited 150 s).
Minimal Reproduction
Any workspace whose app compiles sources from more than ~510 directories outside the workspace root, on macOS:
- Create a workspace in
apps/app and ~600 small libraries in libs/lib-N/src/index.ts, mapped through tsconfig paths and all imported by the app.
ng serve --verbose.
- Edit
libs/lib-599/src/index.ts: no rebuild. Edit libs/lib-1/src/index.ts: rebuild (or the other way around, depending on subscription order).
The limit itself reproduces with @parcel/watcher alone (script in parcel-bundler/watcher#190: subscribing 1,000 sibling folders fails from about 500 on with Error starting FSEvents stream).
Exception or Error
Nothing is printed. The error is swallowed in ensureDirWatched; with an instrumented catch every failing subscription rejects with:
Error: Error starting FSEvents stream
Your Environment
@angular/build 22.2.1 (22.3.0-next.0 has the same ParcelExternalManager)
@angular/cli 22.2.1
@angular/core 22.2.1
@parcel/watcher 2.6.0
Node.js 24.21.0
npm 11.19
macOS 27.0 (26A428), Apple Silicon
Anything else relevant?
Workarounds measured on the same project with the same 22 edits; idle CPU is the dev-server process tree over 60 s:
| Variant |
Streams |
Edits detected |
Idle CPU |
| 22.2.1 as shipped |
511 ok / 767 failed |
12/22 |
0.1 % |
NG_BUILD_WATCH_ROOT=1 |
511 ok / 767 failed |
12/22 |
n/a |
--poll 2500 |
chokidar |
22/22 |
22.9 % of a core |
--poll 1000 |
chokidar |
22/22 |
46.9 % of a core |
| local patch: subscribe the common ancestor of the external directory and the workspace root |
2 ok / 0 failed |
22/22 |
0.1 % |
Suggested fix: avoid one stream per directory on macOS, for example by subscribing a common ancestor (VS Code does the same, see the discussion in parcel-bundler/watcher#190), or by falling back to an ancestor subscription when subscribe rejects. At minimum, log the swallowed error in verbose mode so the problem is visible.
On 21.2.24 (watchpack) edits to these library files rebuilt reliably.
Command
serve
Is this a regression?
The previous version in which this bug was not present was
21.2.24 (watchpack)
Description
ParcelExternalManager.ensureDirWatched(packages/angular/build/src/utils/watcher.ts) callsparcelWatcher.subscribe()once per directory outside the workspace root. On macOS every subscription is its own FSEvents stream, and macOS refuses to start more than about 512 streams per process (parcel-bundler/watcher#190). The rejection is caught and ignored ("Ignore subscription errors for missing or restricted external directories"), so the files in those directories are silently not watched and editing them never triggers a rebuild.Our setup: npm workspaces monorepo; the Angular workspace (angular.json) lives in
<monorepo>/apps/<APP>and the app imports ~100 libraries from<monorepo>/libs/*through tsconfig paths. Oneng serveregisters 1,278 external directories (about 1,000 library source folders plus 274 node_modules packages). Instrumenting thecatchinensureDirWatchedon 22.2.1 (which already contains #34187):Error starting FSEvents streamWhich directories end up watched depends on subscription order. I edited 22 files in one batch with
ng serve --verbose: 1 app file, 11 library files in directories whose subscription succeeded, and 10 in directories whose subscription failed. The 12 in watched directories show up in the modified list and rebuild; the 10 others never do (waited 150 s).Minimal Reproduction
Any workspace whose app compiles sources from more than ~510 directories outside the workspace root, on macOS:
apps/appand ~600 small libraries inlibs/lib-N/src/index.ts, mapped through tsconfigpathsand all imported by the app.ng serve --verbose.libs/lib-599/src/index.ts: no rebuild. Editlibs/lib-1/src/index.ts: rebuild (or the other way around, depending on subscription order).The limit itself reproduces with @parcel/watcher alone (script in parcel-bundler/watcher#190: subscribing 1,000 sibling folders fails from about 500 on with
Error starting FSEvents stream).Exception or Error
Your Environment
Anything else relevant?
Workarounds measured on the same project with the same 22 edits; idle CPU is the dev-server process tree over 60 s:
NG_BUILD_WATCH_ROOT=1--poll 2500--poll 1000Suggested fix: avoid one stream per directory on macOS, for example by subscribing a common ancestor (VS Code does the same, see the discussion in parcel-bundler/watcher#190), or by falling back to an ancestor subscription when
subscriberejects. At minimum, log the swallowed error in verbose mode so the problem is visible.On 21.2.24 (watchpack) edits to these library files rebuilt reliably.