npm / npm/cli

[BUG] npm login exits 1 with no error message when stdin is not interactive

Open
#9,860 0 comments 0 reactions 1 assignee View on GitHub

@martinrrm is already working on this.

Since Aug 25, 2026.

Bug
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:

  1. --auth-type=legacy goes straight to the username prompt and dies in about a second.
  2. 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
  1. npm login --auth-type=legacy < /dev/null
  2. It prints Username: and exits 1 with no error.
  3. 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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.