{"record":{"id":"0358c85ba7c6f0f0","repo":"langgenius/dify","slug":"email-code-error-0358c8","errorCode":"email_code_error","errorMessage":"Email code is invalid or expired.","messagePattern":"Email code is invalid or expired\\.","errorType":"error_code","errorClass":"EmailCodeError","httpStatus":400,"severity":"error","filePath":"api/controllers/console/auth/forgot_password.py","lineNumber":132,"sourceCode":"        is_forgot_password_error_rate_limit = AccountService.is_forgot_password_error_rate_limit(user_email)\n        if is_forgot_password_error_rate_limit:\n            raise EmailPasswordResetLimitError()\n\n        token_data = AccountService.get_reset_password_data(req_data.token)\n        if token_data is None:\n            raise InvalidTokenError()\n\n        token_email = token_data.get(\"email\")\n        if not isinstance(token_email, str):\n            raise InvalidEmailError()\n        normalized_token_email = token_email.lower()\n\n        if user_email != normalized_token_email:\n            raise InvalidEmailError()\n\n        if req_data.code != token_data.get(\"code\"):\n            AccountService.add_forgot_password_error_rate_limit(user_email)\n            raise EmailCodeError()\n\n        # Verified, revoke the first token\n        AccountService.revoke_reset_password_token(req_data.token)\n\n        # Refresh token data by generating a new token\n        _, new_token = AccountService.generate_reset_password_token(\n            token_email, code=req_data.code, additional_data={\"phase\": \"reset\"}\n        )\n\n        AccountService.reset_forgot_password_error_rate_limit(user_email)\n        return {\"is_valid\": True, \"email\": normalized_token_email, \"token\": new_token}\n\n\n@console_ns.route(\"/forgot-password/resets\")\nclass ForgotPasswordResetApi(Resource):\n    @console_ns.doc(\"reset_password\")\n    @console_ns.doc(description=\"Reset password with verification token\")\n    @console_ns.expect(console_ns.models[ForgotPasswordResetPayload.__name__])","sourceCodeStart":114,"sourceCodeEnd":150,"githubUrl":"https://github.com/langgenius/dify/blob/ef8544b173fd6cd7a8e71df2cab576e52bebbfbc/api/controllers/console/auth/forgot_password.py#L114-L150","documentation":"Raised by EmailCodeError in ForgotPasswordCheckApi.post when req_data.code differs from the code stored in the reset token. Each failure increments add_forgot_password_error_rate_limit and can lead to error 412. The code is the pin delivered in the reset email.","triggerScenarios":"POST /console/api/forgot-password/validity with a wrong, stale, or typo'd reset code while the token and email are valid.","commonSituations":"User mistypes the reset code; code from an older reset email is used after a resend; autofill inserts a saved but outdated code; whitespace or formatting in the pasted code.","solutions":["Trim whitespace and enter the code from the most recent reset email.","Request a new reset email if uncertain, and use the new token+code pair together.","Track attempts client-side and warn before the rate-limit threshold.","Disable autofill on the code input to prevent stale-code substitution."],"exampleFix":"// before\ncheckValidity({ email, token, code: rawInput });\n// after\ncheckValidity({ email, token: latestToken, code: rawInput.trim() });","handlingStrategy":"validation","validationCode":"const cleanCode = String(code).trim();\nif (!/^\\d{4,8}$/.test(cleanCode)) {\n  warnInvalidCodeFormat();\n  return;\n}","typeGuard":"function looksLikeResetCode(c) { return typeof c === 'string' && /^\\d{4,8}$/.test(c.trim()); }","tryCatchPattern":"try {\n  await checkValidity({ email, token, code: cleanCode });\n} catch (e) {\n  if (e.code === 'email_code_error') bumpAttempts();\n  else if (e.code === 'email_password_reset_limit') showCooldown();\n  else throw e;\n}","preventionTips":["Trim and validate the code format client-side.","Use the code from the most recent reset email only.","Disable autofill on the code input."],"tags":["auth","forgot-password","verification-code","brute-force-protection"],"backgroundTag":null,"analyzedSha":"ef8544b173fd6cd7a8e71df2cab576e52bebbfbc","analyzedAt":"2026-08-12T05:15:17.394Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}