Is there an existing issue for this?
This issue exists in the latest npm version
Current Behavior
Running npm login where stdin cannot be typed into ends the process with exit code 1 and no error message at all. Nothing on stdout, nothing on stderr. The only trace is the debug log, which says:
error Exit handler never called!
error This is an error with npm itself. Please report this error at:
error <https://github.com/npm/cli/issues>
so I am doing that.
The cause is read. read() only settles its promise on line, error or SIGINT, and has no handler for close. When stdin is at EOF, readline emits close without ever emitting line, so the promise never settles. login in lib/utils/auth.js awaits read.username(...), that await never returns, nothing else is holding the event loop, and node exits. ExitHandler then sees the command never finished and logs the message above.
Proof that the promise is the thing left hanging, run against npm 12.0.2's own bundled copy of read:
const { read } = require('read')
let settled = false
process.on('exit', c => console.log(`exit=${c} settled=${settled}`))
read({ prompt: 'Username:' }).then(() => settled = true, () => settled = true)
$ node t.js < /dev/null
Username: exit=0 settled=false
Two ways to reach it:
--auth-type=legacy goes straight to the username prompt and dies in about a second.
- The default web flow gets there too. The POST to
/-/v1/login succeeds, then the poll on the done URL returns a 4xx (an expired session after a few minutes of polling, or a 403 straight away, I have logs of both). npm-profile maps any 4xx there to WebLoginNotSupported, npm logs "web login not supported, trying couch", and the couch path hits the same prompt and the same dead end.
I am not reporting the couch fallback itself. npm-profile has a test covering exactly that fallback, so it is deliberate. The bug is that the fallback lands on a prompt that can never be answered, and instead of saying so, npm exits silently and blames itself in a log file the user has not opened.
Worth noting the message never reaches the terminal on 12.0.2, redirected or not, because the process exits before the write flushes. On older npm it did print. I first hit this on 10.8.2 where the banner showed up on screen.
Expected Behavior
An error that says what happened, the way npm already does for OTP prompts, where otplease rethrows instead of prompting when stdin or stdout is not a TTY.
Something like npm error canceled, or better, a line pointing at npm token or a NPM_TOKEN / .npmrc for non interactive auth.
What should not happen is exit 1 with no output and a log telling the user npm is broken.
Steps To Reproduce
npm login --auth-type=legacy < /dev/null
- It prints
Username: and exits 1 with no error.
- Open the debug log it never mentions to find "Exit handler never called!".
Same on Windows PowerShell with input redirected from the null device, and in any tool or CI shell that runs npm without an interactive stdin. That is how I ran into it, publishing a package from an agent shell.
I have opened a fix upstream in read, which is where the promise is left hanging: npm/read#157. With that patch the same command prints npm error canceled and exits 1, which is at least an error.
Environment
- npm: 12.0.2 (also 10.8.2)
- Node.js: v22.23.2 (also v20.20.1)
- OS Name: Ubuntu 24.04 on WSL2, and Windows 11
- npm config: defaults, no registry or auth config involved
Not the same as the open "Exit handler never called" reports (#8931, #9739, #8404, #8974, #8766), which are all install and network timeout paths. #7612 was this crash on the login command back in 2024 and was fixed by npm/npm-profile#144; this is a different route to it that the fix did not cover.
Written with AI assistance (Claude Code). The repros, logs and the upstream patch are mine and verified locally.
Is there an existing issue for this?
This issue exists in the latest npm version
Current Behavior
Running
npm loginwhere stdin cannot be typed into ends the process with exit code 1 and no error message at all. Nothing on stdout, nothing on stderr. The only trace is the debug log, which says:so I am doing that.
The cause is
read.read()only settles its promise online,errororSIGINT, and has no handler forclose. When stdin is at EOF, readline emitsclosewithout ever emittingline, so the promise never settles.logininlib/utils/auth.jsawaitsread.username(...), that await never returns, nothing else is holding the event loop, and node exits.ExitHandlerthen sees the command never finished and logs the message above.Proof that the promise is the thing left hanging, run against npm 12.0.2's own bundled copy of read:
Two ways to reach it:
--auth-type=legacygoes straight to the username prompt and dies in about a second./-/v1/loginsucceeds, then the poll on the done URL returns a 4xx (an expired session after a few minutes of polling, or a 403 straight away, I have logs of both). npm-profile maps any 4xx there toWebLoginNotSupported, npm logs "web login not supported, trying couch", and the couch path hits the same prompt and the same dead end.I am not reporting the couch fallback itself. npm-profile has a test covering exactly that fallback, so it is deliberate. The bug is that the fallback lands on a prompt that can never be answered, and instead of saying so, npm exits silently and blames itself in a log file the user has not opened.
Worth noting the message never reaches the terminal on 12.0.2, redirected or not, because the process exits before the write flushes. On older npm it did print. I first hit this on 10.8.2 where the banner showed up on screen.
Expected Behavior
An error that says what happened, the way npm already does for OTP prompts, where
otpleaserethrows instead of prompting when stdin or stdout is not a TTY.Something like
npm error canceled, or better, a line pointing atnpm tokenor aNPM_TOKEN/.npmrcfor non interactive auth.What should not happen is exit 1 with no output and a log telling the user npm is broken.
Steps To Reproduce
npm login --auth-type=legacy < /dev/nullUsername:and exits 1 with no error.Same on Windows PowerShell with input redirected from the null device, and in any tool or CI shell that runs npm without an interactive stdin. That is how I ran into it, publishing a package from an agent shell.
I have opened a fix upstream in read, which is where the promise is left hanging: npm/read#157. With that patch the same command prints
npm error canceledand exits 1, which is at least an error.Environment
Not the same as the open "Exit handler never called" reports (#8931, #9739, #8404, #8974, #8766), which are all install and network timeout paths. #7612 was this crash on the login command back in 2024 and was fixed by npm/npm-profile#144; this is a different route to it that the fix did not cover.
Written with AI assistance (Claude Code). The repros, logs and the upstream patch are mine and verified locally.