{"record":{"id":"b50523906a85910b","repo":"HeyPuter/puter","slug":"account-suspended-b50523","errorCode":"account_suspended","errorMessage":"Account is suspended","messagePattern":"Account is suspended","errorType":"exception","errorClass":"HttpError","httpStatus":403,"severity":"error","filePath":"src/backend/core/http/middleware/userProtected.ts","lineNumber":176,"sourceCode":"    const refreshUser: RequestHandler = async (\n        req: Request,\n        _res: Response,\n        next: NextFunction,\n    ) => {\n        const actor = req.actor;\n        if (!actor?.user?.id)\n            throw new HttpError(401, 'User required', {\n                legacyCode: 'unauthorized',\n            });\n        const user = await userStore.getByProperty('id', actor.user.id, {\n            force: true,\n        });\n        if (!user)\n            throw new HttpError(404, 'User not found', {\n                legacyCode: 'not_found',\n            });\n        if (user.suspended)\n            throw new HttpError(403, 'Account is suspended', {\n                legacyCode: 'account_suspended',\n            });\n        req.userProtected = { user };\n        next();\n    };\n\n    // 3. Password (bcrypt) OR valid OIDC revalidation cookie.\n    //\n    //   - Temp users (no password + no email) pass only when the route was\n    //     registered with `allowTempUsers: true` (delete-own-user).\n    //   - `req.body.password` → bcrypt match against user row. OIDC-only\n    //     accounts bounce with `oidc_revalidation_required` + a\n    //     `revalidate_url` helper so the GUI can open the OIDC popup.\n    //   - Otherwise accept a valid `puter_revalidation` cookie. Expiry,\n    //     `purpose === 'revalidate'`, matching `user_uuid` all required.\n    //   - Password account, neither credential → 403 `password_required`.\n    const verifyIdentity: RequestHandler = async (\n        req: Request,","sourceCodeStart":158,"sourceCodeEnd":194,"githubUrl":"https://github.com/HeyPuter/puter/blob/908ec23eda38526170322c3edf71ba45ecb1ca95/src/backend/core/http/middleware/userProtected.ts#L158-L194","documentation":"Raised in `refreshUser` when the fresh, cache-bypassed user row has `suspended` truthy. The userProtected chain re-reads the user specifically to catch accounts suspended after login, blocking them from sensitive operations.","triggerScenarios":"An account is suspended by an admin or moderation system mid-session, then the user attempts a userProtected action (delete account, change password).","commonSituations":"Moderation action taken while the user is logged in; an automated abuse system suspended the account between login and the sensitive action.","solutions":["Contact support to resolve the suspension.","As admin, clear the `suspended` flag once resolved.","Force the user to re-authenticate after the flag is cleared."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { await call(); }\ncatch (e) {\n  if (e.code === 'account_suspended') { showAccountSuspended(); return; }\n  throw e;\n}","preventionTips":["Force re-authentication after an account is suspended.","Clear the suspended flag only after the issue is resolved.","Treat the cache-bypassing read as authoritative — don't trust stale cached rows."],"tags":["account","suspended","user-protected","moderation"],"backgroundTag":null,"analyzedSha":"908ec23eda38526170322c3edf71ba45ecb1ca95","analyzedAt":"2026-08-12T20:53:15.911Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}