HeyPuter/puter · error · HttpError
unauthorized
unauthorized
Error message
Incorrect password.
What it means
Returned by POST /login when user.password === null — the account exists and is not suspended, but has no password set (legacy code 'unauthorized'). This typically means the account was created through an OAuth/passwordless flow and never had a local password, so a credential login is impossible. It is enforced before bcrypt.compare to avoid passing null to bcrypt.
Source
Thrown at src/backend/controllers/auth/AuthController.ts:422
}
if (
user.username === 'system' &&
!(this.config as { allow_system_login?: boolean })
.allow_system_login
) {
throw new HttpError(
404,
username ? 'Username not found.' : 'Email not found.',
{ legacyCode: 'not_found' },
);
}
if (user.suspended) {
throw new HttpError(401, 'This account is suspended.', {
legacyCode: 'account_suspended',
});
}
if (user.password === null) {
throw new HttpError(401, 'Incorrect password.', {
legacyCode: 'unauthorized',
});
}
// Verify password
const passwordMatch = await bcrypt.compare(
password,
user.password as string,
);
if (!passwordMatch) {
throw new HttpError(401, 'Incorrect password.', {
legacyCode: 'password_mismatch',
});
}
const reauthAuthId = this.#extractAuthIdFromReauthToken(
req.body.reauth_token,
);View on GitHub (pinned to 908ec23eda)
Solutions
- Use the same OAuth/passwordless flow the account was created with (e.g. puter.auth.signIn()).
- Set a password on the account first via the password-reset or profile flow, then retry credential login.
- If migrating accounts, backfill passwords before exposing credential login.
Example fix
// before: account has no password set
await fetch('/login', { method:'POST', body:JSON.stringify({ username, password }) }); // -> 401 unauthorized
// after: use the OAuth flow the account was created with
const token = await puter.auth.signIn(); Defensive patterns
Strategy: try-catch
Try / catch
try { await login({ username, password }); }
catch (e) {
if (e.code === 'unauthorized' && /password/i.test(e.message)) {
/* account has no password — route to OAuth/passwordless sign-in */
} else throw e;
} Prevention
- Detect OAuth-only accounts and route them to puter.auth.signIn() instead of password login.
- Offer a 'set a password' flow for accounts that want credential login.
When it happens
Trigger: An account created via SSO/OAuth trying to log in by password; a migrated account whose password was never set; an account that had its password cleared.
Common situations: User signed up with Google/GitHub and now tries password login; admin created an account without setting a password; password-reset flow left password null.
Related errors
AI-assisted analysis of HeyPuter/puter@908ec23eda (2026-08-12).
Data as JSON: /api/errors/e3ebfb87054bd272.
Report an issue: GitHub.