Skip to content

fix: prevent duplicate process.exit() on concurrent unhandledRejections#9751

Open
Gopi-Agasthia-S-005CIU744 wants to merge 1 commit into
npm:latestfrom
Gopi-Agasthia-S-005CIU744:fix/issue-9739
Open

fix: prevent duplicate process.exit() on concurrent unhandledRejections#9751
Gopi-Agasthia-S-005CIU744 wants to merge 1 commit into
npm:latestfrom
Gopi-Agasthia-S-005CIU744:fix/issue-9739

Conversation

@Gopi-Agasthia-S-005CIU744

Copy link
Copy Markdown

When multiple package downloads time out simultaneously (e.g. from a private
Artifactory registry), Node.js fires multiple unhandledRejection events in
quick succession. Each one enters #handleExit, which defers process.exit()
via a stderr.write → stdout.write callback chain.

Without a re-entrancy guard, both deferred callbacks complete independently:

  1. First rejection sets #exited = true, queues a deferred process.exit().
  2. Second rejection enters #handleExit before any I/O callback has run,
    sets #exited = true again, queues a second deferred process.exit().
  3. First process.exit() fires → emits 'exit'#handleProcessExitAndReset
    resets #exited back to false.
  4. Second process.exit() fires → emits 'exit'#handleProcessExit sees
    #exited === false → logs "Exit handler never called!".

Fix adds a two-line re-entrancy guard at the top of #handleExit so only the
first rejection ever schedules a process.exit().

The regression test defers stderr.write via setImmediate to open the same
race window that real I/O creates in production, fires two unhandledRejection
events synchronously, and asserts process.exit() was called exactly once.
The test fails without the fix and passes with it.

References

Fixes #9739

When multiple package downloads time out simultaneously (e.g. from a
private Artifactory registry), each ETIMEDOUT rejection fires an
unhandledRejection event. Without a re-entrancy guard, each call to
#handleExit queues its own process.exit() via the deferred stderr/stdout
write-callback chain. The second process.exit() fires after #exited has
already been reset by the first, causing #handleProcessExit to log
"Exit handler never called!".

Fix by returning early in #handleExit when #exited is already true so
only the first rejection ever schedules a process.exit().
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

npm install crashes with "Exit handler never called!" after multiple package download timeouts from Artifactory registry

1 participant