Did you read the migration guide?
Is there an existing issue that is already proposing this?
Potential Commit/PR that introduced the regression
The signal handler was added somewhere between v11.0.16 and v11.0.17 in actions/start.action.js.
Versions
11.0.16 -> 11.0.17 (also affects 11.0.18, 11.0.19)
Describe the regression
Since @nestjs/[email protected], nest start --watch hangs after receiving SIGINT (Ctrl+C). The child NestJS application exits cleanly (port is freed, shutdown hooks complete), but the CLI process itself stays alive indefinitely. A second Ctrl+C is required to exit.
A signal handler was added in v11.0.17 (not present in v11.0.16) in actions/start.action.js:
const signalHandler = (signal) => {
if (childProcessRef) {
childProcessRef.kill(signal);
}
else {
process.exit();
}
};
process.on('SIGINT', signalHandler);
process.on('SIGTERM', signalHandler);
When SIGINT is received:
- The handler forwards the signal to the child process:
childProcessRef.kill(signal)
- The child exits and the
exit handler sets childProcessRef = undefined
- But the CLI process itself never exits — the file watcher (chokidar/tsc) keeps the event loop alive
- On a second Ctrl+C,
childProcessRef is undefined, so process.exit() is finally called
In v11.0.16, this signal handler did not exist. SIGINT was delivered directly to the process group, the child process terminated via the default handler, and the CLI process also exited naturally via the default SIGINT behavior.
Minimum reproduction code
# Any NestJS project
npx @nestjs/[email protected] start --watch
# Press Ctrl+C → process hangs
# Press Ctrl+C again → process finally exits
# Compare with:
npx @nestjs/[email protected] start --watch
# Press Ctrl+C → exits immediately
Expected behavior
nest start --watch should exit on a single Ctrl+C, as it did in v11.0.16.
A possible fix — exit the CLI when the child exits after a signal was received:
let shuttingDown = false;
const signalHandler = (signal) => {
shuttingDown = true;
if (childProcessRef) {
childProcessRef.kill(signal);
} else {
process.exit();
}
};
// ...
childProcessRef.on('exit', (code) => {
process.exitCode = code;
childProcessRef = undefined;
if (shuttingDown) {
process.exit(code ?? 0);
}
});
Other
- OS: macOS 25.3.0 (arm64)
- Node.js: v24.14.0
- Package manager: pnpm 10.32.1
- Builder: Both
tsc and swc are affected (the signal handler is in start.action.js, shared by all builders)
Did you read the migration guide?
Is there an existing issue that is already proposing this?
Potential Commit/PR that introduced the regression
The signal handler was added somewhere between v11.0.16 and v11.0.17 in
actions/start.action.js.Versions
11.0.16 -> 11.0.17 (also affects 11.0.18, 11.0.19)
Describe the regression
Since
@nestjs/[email protected],nest start --watchhangs after receiving SIGINT (Ctrl+C). The child NestJS application exits cleanly (port is freed, shutdown hooks complete), but the CLI process itself stays alive indefinitely. A second Ctrl+C is required to exit.A signal handler was added in v11.0.17 (not present in v11.0.16) in
actions/start.action.js:When SIGINT is received:
childProcessRef.kill(signal)exithandler setschildProcessRef = undefinedchildProcessRefisundefined, soprocess.exit()is finally calledIn v11.0.16, this signal handler did not exist. SIGINT was delivered directly to the process group, the child process terminated via the default handler, and the CLI process also exited naturally via the default SIGINT behavior.
Minimum reproduction code
Expected behavior
nest start --watchshould exit on a single Ctrl+C, as it did in v11.0.16.A possible fix — exit the CLI when the child exits after a signal was received:
Other
tscandswcare affected (the signal handler is instart.action.js, shared by all builders)