{"record":{"id":"641e50a2b3d9657b","repo":"langgenius/dify","slug":"email-code-error-641e50","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/login.py","lineNumber":303,"sourceCode":"\n        original_email = req_data.email\n        user_email = original_email.lower()\n        language = req_data.language\n\n        token_data = AccountService.get_email_code_login_data(req_data.token)\n        if token_data is None:\n            _log_console_login_failure(email=user_email, reason=LoginFailureReason.INVALID_EMAIL_CODE_TOKEN)\n            raise InvalidTokenError()\n\n        token_email = token_data.get(\"email\")\n        normalized_token_email = token_email.lower() if isinstance(token_email, str) else token_email\n        if normalized_token_email != user_email:\n            _log_console_login_failure(email=user_email, reason=LoginFailureReason.EMAIL_CODE_EMAIL_MISMATCH)\n            raise InvalidEmailError()\n\n        if token_data[\"code\"] != req_data.code:\n            _log_console_login_failure(email=user_email, reason=LoginFailureReason.INVALID_EMAIL_CODE)\n            raise EmailCodeError()\n\n        AccountService.revoke_email_code_login_token(req_data.token)\n        try:\n            account = _get_account_with_case_fallback(original_email)\n        except Unauthorized as exc:\n            _log_console_login_failure(email=user_email, reason=LoginFailureReason.ACCOUNT_BANNED)\n            raise AccountBannedError() from exc\n        except AccountRegisterError:\n            _log_console_login_failure(email=user_email, reason=LoginFailureReason.ACCOUNT_IN_FREEZE)\n            raise AccountInFreezeError()\n        if account:\n            tenants = TenantService.get_join_tenants(account, session=db.session())\n            if not tenants:\n                workspaces = FeatureService.get_license().workspaces\n                if not workspaces.is_available():\n                    raise WorkspacesLimitExceeded()\n                if not FeatureService.is_workspace_creation_allowed():\n                    raise NotAllowedCreateWorkspace()","sourceCodeStart":285,"sourceCodeEnd":321,"githubUrl":"https://github.com/langgenius/dify/blob/ef8544b173fd6cd7a8e71df2cab576e52bebbfbc/api/controllers/console/auth/login.py#L285-L321","documentation":"Raised by POST /console/api/email-code-login/validity (HTTP 400, code email_code_error) when the token is valid and the email matches, but token_data['code'] != req_data.code. The one-time code submitted by the user does not equal the code stored in the token data.","triggerScenarios":"POST /console/api/email-code-login/validity with a wrong, mistyped, or expired-but-still-present code. The token data is intact, the email matches, but the code field differs.","commonSituations":"User mistyped the numeric code; code from a previous email; OCR/copy error; client applied the wrong code transformation (e.g., the @decrypt_code_field decorator expects an encrypted code that the client sent in plaintext or vice-versa).","solutions":["Request and enter a fresh code, typing carefully.","Verify the client encrypts the code the same way @decrypt_code_field expects (check RSA public key / encryption scheme).","Make sure the user is reading the most recent email, not an older one.","If the code is correct but still rejected, confirm the token has not already been consumed by a concurrent validity call."],"exampleFix":"// before\nfetch('/console/api/email-code-login/validity', {body: JSON.stringify({email, code: rawCode, token})})\n// after - encrypt the code field to match @decrypt_code_field\nconst encryptedCode = await encryptWithPublicKey(serverPublicKey, rawCode)\nfetch('/console/api/email-code-login/validity', {body: JSON.stringify({email, code: encryptedCode, token})})","handlingStrategy":"validation","validationCode":"# Validate the code format before submitting and ensure it is encrypted\nif not code or not code.isdigit() or len(code) != EXPECTED_CODE_LEN:\n    show_error('Enter the full numeric code from the email.')\n    return\nencrypted_code = encrypt_for_server(code)\nsubmit_validity(email, encrypted_code, token)","typeGuard":"null","tryCatchPattern":"from controllers.console.auth.error import EmailCodeError\ntry:\n    verify_code(email, code, token)\nexcept EmailCodeError:\n    prompt('The code is wrong; request a new one if unsure.')","preventionTips":["Confirm the client encrypts the code with the server's public key (matches @decrypt_code_field).","Read codes from the most recent email only.","Prevent double-submission that could consume or revoke the token prematurely."],"tags":["auth","email-code-login","code","validation","otp"],"backgroundTag":null,"analyzedSha":"ef8544b173fd6cd7a8e71df2cab576e52bebbfbc","analyzedAt":"2026-08-12T05:15:17.394Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}