pnpm/pnpm · error · LoginNoTokenError

LOGIN_NO_TOKEN

LOGIN_NO_TOKEN

Error message

The registry did not return an authentication token

What it means

The registry answered the adduser request successfully but the response contained no usable token (AddUserNoTokenError), so pnpm has nothing to store and throws LOGIN_NO_TOKEN.

Source

Thrown at pnpm11/auth/commands/src/login.ts:363

      try {
        const result = await addUser({
          username,
          password,
          email,
          otp,
          registryUrl: registry,
          fetch,
        })
        return result.token
      } catch (err) {
        if (err instanceof AddUserHttpError) {
          if (err.status === 401 && err.responseHeaders.get('www-authenticate')?.includes('otp')) {
            throw SyntheticOtpError.fromUnknownBody(globalWarn, err.responseJson)
          }
          throw new ClassicLoginError(err.status, err.responseText)
        }
        if (err instanceof AddUserNoTokenError) {
          throw new LoginNoTokenError()
        }
        throw err
      }
    },
  })

  globalInfo(`Logged in as ${username}`)

  return token
}

class LoginNonInteractiveError extends PnpmError {
  constructor () {
    super('LOGIN_NON_INTERACTIVE', 'The login command requires an interactive terminal')
  }
}

class LoginInvalidResponseError extends PnpmError {

View on GitHub (pinned to 5b11d3a15b)

Solutions

  1. Create a token out-of-band (registry website → Access Tokens) and configure it: `pnpm config set //<registry-host>/:_authToken <token>`
  2. Upgrade the registry software so its adduser response includes a token
  3. Verify the endpoint shape: `curl -u user:pass <registry>/-/user/org.couchdb.user:user` should return an object containing `token`

Example fix

# before
pnpm login  # registry 2xx but no token in body

# after
pnpm config set //registry.example.com/:_authToken eyJhbGciOi...
pnpm whoami  # verify
Defensive patterns

Strategy: fallback

Validate before calling

const res = await fetch(`${registry}/-/user/org.couchdb.user:${encodeURIComponent(user)}`, {
  headers: { authorization: `Basic ${Buffer.from(`${user}:${pass}`).toString('base64')}` },
})
const body = await res.json().catch(() => null)
if (res.ok && typeof body?.token !== 'string' || body?.token === '') {
  console.error('registry does not return tokens from adduser; using manual _authToken')
  await exec(`pnpm config set ${registryKey}:_authToken ${process.env.NPM_TOKEN}`)
}

Try / catch

try {
  token = await runClassicLogin()
} catch (err) {
  if (err instanceof Error && err.message.includes('LOGIN_NO_TOKEN')) {
    // fallback: out-of-band token created on the registry website
    await exec(`pnpm config set ${registryKey}:_authToken ${process.env.NPM_TOKEN}`)
    token = process.env.NPM_TOKEN
  } else {
    throw err
  }
}

Prevention

When it happens

Trigger: `pnpm login` against a registry whose adduser endpoint returns 2xx with a body missing the `token` field (or an empty one).

Common situations: Misimplemented private registries or custom auth bridges; proxies stripping response fields; schema drift between the registry's adduser response and pnpm's parser.

Understand the failure class

Related errors


AI-assisted analysis of pnpm/pnpm@5b11d3a15b (2026-08-16). Data as JSON: /api/errors/3f1bd529eecf6721. Report an issue: GitHub.