{"record":{"id":"a603045d7104d811","repo":"langgenius/dify","slug":"invalid-email-a60304","errorCode":"invalid_email","errorMessage":"The email address is not valid.","messagePattern":"The email address is not valid\\.","errorType":"error_code","errorClass":"InvalidEmailError","httpStatus":400,"severity":"error","filePath":"api/controllers/console/auth/forgot_password.py","lineNumber":124,"sourceCode":"    @console_ns.response(400, \"Invalid code or token\")\n    @setup_required\n    @email_password_login_enabled\n    @model_validate(ForgotPasswordCheckPayload)\n    def post(self, req_data: ForgotPasswordCheckPayload):\n\n        user_email = req_data.email.lower()\n\n        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)","sourceCodeStart":106,"sourceCodeEnd":142,"githubUrl":"https://github.com/langgenius/dify/blob/ef8544b173fd6cd7a8e71df2cab576e52bebbfbc/api/controllers/console/auth/forgot_password.py#L106-L142","documentation":"Raised by InvalidEmailError in ForgotPasswordCheckApi.post when token_data.get('email') is not a str instance. The reset token's payload is expected to carry an email string; a non-string (None, dict, list) indicates the token was tampered with, mis-issued, or stored in a legacy/corrupt format. Defensive type check before lowercasing.","triggerScenarios":"POST /console/api/forgot-password/validity with a token that resolves to data whose 'email' field is missing or not a string. Distinct from error 415 (email mismatch): this fires before any comparison.","commonSituations":"Token payload corrupted in the store; a manually crafted token that decodes but lacks the email claim; migration between token formats leaving legacy tokens without email; a bug in token generation omitting the email field.","solutions":["Restart the forgot-password flow to obtain a token known to carry a valid email claim.","If reproducible across users, inspect the token-generation code path (generate_reset_password_token) for a missing email field.","Check the token store backend for corruption or partial writes.","Add server-side logging of the token payload shape when this fires to diagnose issuance bugs."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await checkValidity({ email, token, code });\n} catch (e) {\n  if (e.code === 'invalid_email') await restartResetFlow(email); // likely corrupt token\n  else throw e;\n}","preventionTips":["Treat a persistent invalid_email on a fresh token as a server-side issuance bug; file an issue with logs.","Restart the reset flow to get a token with a valid email claim.","Add server-side payload logging when this guard fires."],"tags":["auth","forgot-password","token","data-corruption","validation"],"backgroundTag":null,"analyzedSha":"ef8544b173fd6cd7a8e71df2cab576e52bebbfbc","analyzedAt":"2026-08-12T05:15:17.394Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}