[BUG] npm login exits 1 with no error message when stdin is not interactive
@martinrrm is already working on this.
Since Aug 25, 2026.
- Dominant language
- JavaScript
- Stars
- 10.1k
- Forks
- 4.7k
- Avg merge
- 2d 2h
- Merged PRs (30d)
- 19
Description
Is there an existing issue for this?
- I have searched the existing issues
This issue exists in the latest npm version
- I am using the latest npm
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=legacygoes straight to the username prompt and dies in about a second.- The default web flow gets there too. The POST to
/-/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 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: https://github.com/npm/read/pull/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.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.