fix: settle the promise when the input stream ends - #157
Open
1yakub wants to merge 1 commit into
Open
Conversation
2 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
read()returns a promise that never settles if the input stream ends before a line arrives. There is a handler forline, one forerrorand one forSIGINT, but none forclose, and readline emitscloseon its own the moment the input hits EOF. Nothing rejects, nothing resolves, so the caller waits on a promise that can never finish. Once nothing else is holding the event loop the process just exits, and whatever was waiting is gone with no error anywhere.That happens any time stdin is not something a person can type into, which is common:
< /dev/null, an already closed pipe, a child process spawned without stdin, most CI shells.Where I hit it:
npm loginin a non interactive shell. npm prompts for a username, the prompt can never be answered, and npm exits 1 printing no error at all. Its own debug log says "Exit handler never called! This is an error with npm itself", which is npm noticing that the command promise never resolved. Repro with npm 12.0.2 on Node 22.23.2:Proof that the promise is the thing left hanging, run against npm's own bundled copy of read:
The fix adds the missing
closehandler and rejects withcanceled, the same errorSIGINTalready uses, since both mean the same thing: no answer is coming. Afinishedflag keeps the deliberaterl.close()indone()from rejecting a promise that already resolved.Two tests: one that a stream ending with no line rejects, and one that a stream ending after a line still resolves, which is the case that would break if the close handler fired unconditionally. Without the source change the first test hangs until the runner cancels it and the suite exits 1; with it the suite is 6/6 and lint is clean.
Checked with
npm logintoo: patching only this package inside an installed npm 12.0.2 turns that silent exit 1 intonpm error canceled.Written with AI assistance (Claude Code); the repro, the fix and the test runs are my own and verified locally.