Command
build
Is this a regression?
The previous version in which this bug was not present was
No response
Description
Symptom: After a one-shot ng build (no --watch) completes successfully, the Node process does not exit. process._getActiveHandles() shows a ChildProcess for the esbuild service
all services stopped
handles: [ 'WriteStream', 'WriteStream', 'ReadStream', 'ChildProcess' ]
requests: []
child: {
pid: 52715,
file: '/my/machine/<project>/node_modules/.pnpm/@esbuild+linux-x64@0.27.3/node_modules/@esbuild/linux-x64/bin/esbuild',
args: [
'/my/machine/<project>/node_modules/.pnpm/@esbuild+linux-x64@0.27.3/node_modules/@esbuild/linux-x64/bin/esbuild',
'--service=0.27.3',
'--ping'
]
}
The root cause lays in @angular/build/src/builders/application/build-action.js — runEsBuildBuildAction:
- For
watch:false, BundlerContext.incremental is false, so the build path uses one-shot esbuild.build() and never creates an esbuild.context().
ExecutionResult.dispose() → BundlerContext.dispose() only awaits this.#esbuildContext?.dispose(), which is undefined in this path. So nothing tears down esbuild's long-lived
service child.
- The builder never calls
esbuild.stop() anywhere.
- The plugin teardown registered via
build.onDispose(...) in compiler-plugin.js (javascriptTransformer.close(), cacheStore?.close(), compilation.close?.()) is fire-and-forget
(void ...) and scheduled by esbuild via setTimeout(cb, 0), so it can race with the function returning.
The issue is not apparent in regular builds since the process is exited after the build loop marks done: true but builders that decorate over the Angular builder inherit the leakage.
Minimal Reproduction
Here's how we use the internal builder programatically: https://github.com/native-federation/angular-adapter/blob/8bd8b58b7db8ae3cac5818c2d643077a8b3161ce/packages/angular/src/builders/build/builder.ts#L95
Exception or Error
Your Environment
> ng version
_ _ ____ _ ___
/ \ _ __ __ _ _ _| | __ _ _ __ / ___| | |_ _|
/ △ \ | '_ \ / _` | | | | |/ _` | '__| | | | | | |
/ ___ \| | | | (_| | |_| | | (_| | | | |___| |___ | |
/_/ \_\_| |_|\__, |\__,_|_|\__,_|_| \____|_____|___|
|___/
Angular CLI : 21.2.10
Angular : 21.2.12
Node.js : 24.11.1
Package Manager : pnpm 10.33.0
Operating System : linux x64
┌───────────────────────────────┬───────────────────┬───────────────────┐
│ Package │ Installed Version │ Requested Version │
├───────────────────────────────┼───────────────────┼───────────────────┤
│ @angular-devkit/build-angular │ 21.2.10 │ ^21.2.10 │
│ @angular/build │ 21.2.10 │ ^21.2.10 │
│ @angular/cli │ 21.2.10 │ ^21.2.10 │
│ @angular/common │ 21.2.12 │ ^21.2.0 │
│ @angular/compiler │ 21.2.12 │ ^21.2.0 │
│ @angular/compiler-cli │ 21.2.12 │ ^21.2.0 │
│ @angular/core │ 21.2.12 │ ^21.2.0 │
│ @angular/forms │ 21.2.12 │ ^21.2.0 │
│ @angular/platform-browser │ 21.2.12 │ ^21.2.0 │
│ @angular/router │ 21.2.12 │ ^21.2.0 │
│ rxjs │ 7.8.2 │ ~7.8.0 │
│ typescript │ 5.9.3 │ ~5.9.2 │
│ vitest │ 4.1.5 │ ^4.0.8 │
└───────────────────────────────┴───────────────────┴───────────────────┘
Anything else relevant?
This is only happening when the buildAction is called programatically
Command
build
Is this a regression?
The previous version in which this bug was not present was
No response
Description
Symptom: After a one-shot ng build (no --watch) completes successfully, the Node process does not exit.
process._getActiveHandles()shows a ChildProcess for the esbuild serviceThe root cause lays in @angular/build/src/builders/application/build-action.js —
runEsBuildBuildAction:watch:false,BundlerContext.incrementalisfalse, so the build path uses one-shotesbuild.build()and never creates anesbuild.context().ExecutionResult.dispose()→BundlerContext.dispose()only awaitsthis.#esbuildContext?.dispose(), which isundefinedin this path. So nothing tears down esbuild's long-livedservice child.
esbuild.stop()anywhere.build.onDispose(...)in compiler-plugin.js (javascriptTransformer.close(), cacheStore?.close(), compilation.close?.()) is fire-and-forget(void ...) and scheduled by esbuild via
setTimeout(cb, 0), so it can race with the function returning.The issue is not apparent in regular builds since the process is exited after the build loop marks
done: truebut builders that decorate over the Angular builder inherit the leakage.Minimal Reproduction
Here's how we use the internal builder programatically: https://github.com/native-federation/angular-adapter/blob/8bd8b58b7db8ae3cac5818c2d643077a8b3161ce/packages/angular/src/builders/build/builder.ts#L95
Exception or Error
Your Environment
Anything else relevant?
This is only happening when the buildAction is called programatically