Skip to content

fix(start): forward signals to child and exit watcher on shutdown#3422

Merged
kamilmysliwiec merged 1 commit into
nestjs:masterfrom
maruthang:fix/issue-3158-shutdown-hooks
May 1, 2026
Merged

fix(start): forward signals to child and exit watcher on shutdown#3422
kamilmysliwiec merged 1 commit into
nestjs:masterfrom
maruthang:fix/issue-3158-shutdown-hooks

Conversation

@maruthang

Copy link
Copy Markdown
Contributor

PR Checklist

Please check if your PR fulfills the following requirements:

PR Type

What kind of change does this PR introduce?

[x] Bugfix
[ ] Feature
[ ] Code style update (formatting, local variables)
[ ] Refactoring (no functional changes, no api changes)
[ ] Build related changes
[ ] CI related changes
[ ] Other... Please describe:

What is the current behavior?

Issue Number: 3158

After the reverts that landed in v11.0.20 / v11.0.21 (commit 705bb7da), nest start no longer forwards SIGINT / SIGTERM to the spawned child Node process. As a result, the application's asynchronous shutdown hooks (onModuleDestroy, beforeApplicationShutdown, onApplicationShutdown) never run when the user hits Ctrl+C, because the parent CLI process exits immediately and the child is killed by the terminal's signal broadcast before it can finish its shutdown sequence (or, when launched detached, is left orphaned).

A closely related regression (#3391) makes nest start --watch require two Ctrl+C presses before the process actually exits — the chokidar/tsc watcher keeps the parent's event loop alive after the child has already exited.

What is the new behavior?

  • SIGINT and SIGTERM received by the CLI parent are now forwarded to the spawned child Node process, so async shutdown hooks run to completion before the process exits.
  • The parent waits for the child's exit event and then propagates the same exit code, instead of returning immediately.
  • A shuttingDown flag tracks whether a shutdown signal has been received. In watch mode, when the child exits while shuttingDown is true, the parent explicitly calls process.exit() so the watcher does not keep the event loop alive — fixing the double-Ctrl+C behavior reported in nest start --watch hangs on SIGINT since v11.0.17 #3391.
  • Signal listeners are registered with process.once to avoid double-handling on repeated signals, and the same handler tears down the watcher cleanly.

This re-applies the fix from PRs #3313 / #3338 (which were reverted in 705bb7da) and folds in the watch-mode fix for #3391 in a single, minimal change.

A new unit test file test/actions/start.action.spec.ts covers:

  • SIGINT forwarding to child
  • SIGTERM forwarding to child
  • parent propagates child exit code
  • watch-mode parent exits after child exits during shutdown
  • listeners are not duplicated across multiple signals
  • non-watch mode does not call process.exit when child exits naturally
  • handler is a no-op if no child has been spawned yet

Does this PR introduce a breaking change?

[ ] Yes
[x] No

Other information

This PR also addresses #3391 (nest start --watch requires two Ctrl+C presses) — the same shuttingDown flag used to forward signals is reused to short-circuit the watcher's keep-alive on shutdown, so both regressions are resolved by one change.

re-apply signal forwarding from prs nestjs#3313/nestjs#3338 that was reverted
in 705bb7d, and explicitly exit when the spawned child exits during
shutdown so the chokidar/tsc watcher does not keep the event loop
alive.

closes nestjs#3158
refs nestjs#3391
@kamilmysliwiec
kamilmysliwiec merged commit cabbe0b into nestjs:master May 1, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants