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
- Create a token out-of-band (registry website → Access Tokens) and configure it: `pnpm config set //<registry-host>/:_authToken <token>`
- Upgrade the registry software so its adduser response includes a token
- 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
- Contract-check that the registry's adduser response includes a token before relying on interactive login
- Keep a website-issued access token as the automation path for registries with broken adduser
- Upgrade self-hosted registries so login responses carry a token field
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
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
AI-assisted analysis of pnpm/pnpm@5b11d3a15b (2026-08-16).
Data as JSON: /api/errors/3f1bd529eecf6721.
Report an issue: GitHub.