{"record":{"id":"e3ebfb87054bd272","repo":"HeyPuter/puter","slug":"unauthorized","errorCode":"unauthorized","errorMessage":"Incorrect password.","messagePattern":"Incorrect password\\.","errorType":"http","errorClass":"HttpError","httpStatus":401,"severity":"error","filePath":"src/backend/controllers/auth/AuthController.ts","lineNumber":422,"sourceCode":"        }\n        if (\n            user.username === 'system' &&\n            !(this.config as { allow_system_login?: boolean })\n                .allow_system_login\n        ) {\n            throw new HttpError(\n                404,\n                username ? 'Username not found.' : 'Email not found.',\n                { legacyCode: 'not_found' },\n            );\n        }\n        if (user.suspended) {\n            throw new HttpError(401, 'This account is suspended.', {\n                legacyCode: 'account_suspended',\n            });\n        }\n        if (user.password === null) {\n            throw new HttpError(401, 'Incorrect password.', {\n                legacyCode: 'unauthorized',\n            });\n        }\n\n        // Verify password\n        const passwordMatch = await bcrypt.compare(\n            password,\n            user.password as string,\n        );\n        if (!passwordMatch) {\n            throw new HttpError(401, 'Incorrect password.', {\n                legacyCode: 'password_mismatch',\n            });\n        }\n\n        const reauthAuthId = this.#extractAuthIdFromReauthToken(\n            req.body.reauth_token,\n        );","sourceCodeStart":404,"sourceCodeEnd":440,"githubUrl":"https://github.com/HeyPuter/puter/blob/908ec23eda38526170322c3edf71ba45ecb1ca95/src/backend/controllers/auth/AuthController.ts#L404-L440","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: account has no password set\nawait fetch('/login', { method:'POST', body:JSON.stringify({ username, password }) }); // -> 401 unauthorized\n\n// after: use the OAuth flow the account was created with\nconst token = await puter.auth.signIn();","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { await login({ username, password }); }\ncatch (e) {\n  if (e.code === 'unauthorized' && /password/i.test(e.message)) {\n    /* account has no password — route to OAuth/passwordless sign-in */\n  } else throw e;\n}","preventionTips":["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."],"tags":["auth","login","password","oauth","unauthorized"],"backgroundTag":null,"analyzedSha":"908ec23eda38526170322c3edf71ba45ecb1ca95","analyzedAt":"2026-08-12T20:53:15.911Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}