{"record":{"id":"3fde8767852408e1","repo":"HeyPuter/puter","slug":"email-confirmation-required","errorCode":"email_confirmation_required","errorMessage":"Please confirm your email to continue","messagePattern":"Please confirm your email to continue","errorType":"exception","errorClass":"HttpError","httpStatus":403,"severity":"error","filePath":"src/backend/core/http/middleware/gates.ts","lineNumber":372,"sourceCode":" *\n * Throws 403 with a per-gate legacy code (`email_confirmation_required` /\n * `phone_verification_required` / `card_verification_required`) so clients can\n * show the right prompt instead of a generic error. There is no state where a\n * user should be let in with any verification pending, so the first pending\n * gate rejects.\n */\nexport const assertVerifiedAccount = (\n    user:\n        | {\n              requires_email_confirmation?: unknown;\n              email_confirmed?: unknown;\n              requires_phone_verification?: unknown;\n              requires_card_verification?: unknown;\n          }\n        | undefined,\n): void => {\n    if (user?.requires_email_confirmation && !user?.email_confirmed) {\n        throw new HttpError(403, 'Please confirm your email to continue', {\n            legacyCode: 'email_confirmation_required',\n        });\n    }\n    if (user?.requires_phone_verification) {\n        throw new HttpError(\n            403,\n            'Please verify your phone number to continue',\n            {\n                legacyCode: 'phone_verification_required' as never,\n            },\n        );\n    }\n    if (user?.requires_card_verification) {\n        throw new HttpError(403, 'Please verify your card to continue', {\n            legacyCode: 'card_verification_required' as never,\n        });\n    }\n};","sourceCodeStart":354,"sourceCodeEnd":390,"githubUrl":"https://github.com/HeyPuter/puter/blob/908ec23eda38526170322c3edf71ba45ecb1ca95/src/backend/core/http/middleware/gates.ts#L354-L390","documentation":"Raised by `assertVerifiedAccount` when the user row has `requires_email_confirmation` truthy AND `email_confirmed` falsy. The account is gated until the user confirms their email address.","triggerScenarios":"A newly registered account that has not clicked the confirmation link hits a route guarded by `assertVerifiedAccount`; or an admin toggled `requires_email_confirmation` on for existing accounts.","commonSituations":"Confirmation email landed in spam / never opened; the flag was enabled server-side for compliance on a feature the user is trying to reach; user changed email and hasn't re-confirmed.","solutions":["Open the confirmation link sent to the account's email.","Trigger a resend-confirmation flow if the link expired.","As admin, set `requires_email_confirmation=false` or `email_confirmed=true` if the gate is misapplied.","Verify the email_confirmed flag actually flipped after confirmation."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// If the API surfaces the user object, gate the action client-side:\nif (user.requires_email_confirmation && !user.email_confirmed) {\n  promptEmailConfirmation();\n}","typeGuard":"const needsEmailConfirmation = (u) => !!(u && u.requires_email_confirmation && !u.email_confirmed);","tryCatchPattern":"try { await call(); }\ncatch (e) {\n  if (e.code === 'email_confirmation_required') { await resendConfirmation(); return; }\n  throw e;\n}","preventionTips":["Confirm email immediately after signup.","Resend confirmation if the link may have expired.","Surface the unconfirmed state in the UI before the gated action."],"tags":["email","verification","account","gate"],"backgroundTag":null,"analyzedSha":"908ec23eda38526170322c3edf71ba45ecb1ca95","analyzedAt":"2026-08-12T20:53:15.911Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}