Skip to content

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

Description

@1yakub

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions