{"record":{"id":"21298d45f77004b1","repo":"langgenius/dify","slug":"invalid-or-expired-token-21298d","errorCode":"invalid_or_expired_token","errorMessage":"The token is invalid or has expired.","messagePattern":"The token is invalid or has expired\\.","errorType":"error_code","errorClass":"InvalidTokenError","httpStatus":400,"severity":"error","filePath":"api/controllers/console/auth/login.py","lineNumber":293,"sourceCode":"\n\n@console_ns.route(\"/email-code-login/validity\")\nclass EmailCodeLoginApi(Resource):\n    @setup_required\n    @console_ns.expect(console_ns.models[EmailCodeLoginPayload.__name__])\n    @console_ns.response(200, \"Success\", console_ns.models[SimpleResultResponse.__name__])\n    @decrypt_code_field\n    @model_validate(EmailCodeLoginPayload)\n    def post(self, req_data: EmailCodeLoginPayload):\n\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:","sourceCodeStart":275,"sourceCodeEnd":311,"githubUrl":"https://github.com/langgenius/dify/blob/ef8544b173fd6cd7a8e71df2cab576e52bebbfbc/api/controllers/console/auth/login.py#L275-L311","documentation":"Raised by POST /console/api/email-code-login/validity (HTTP 400, code invalid_or_expired_token) when AccountService.get_email_code_login_data(token) returns None. TokenManager has no token data for the given token under the 'email_code_login' type, meaning it was never issued, already revoked, or its TTL elapsed.","triggerScenarios":"POST /console/api/email-code-login/validity with a token that TokenManager.get_token_data cannot resolve in Redis under the 'email_code_login' namespace. Caused by expiry, revocation, typo, or a token minted for a different token type.","commonSituations":"User waited too long before entering the code; user clicked an old login link after the token expired; token already consumed by a prior validity call (note revoke happens AFTER this check, so reuse is possible, but a prior successful flow revokes it); Redis eviction/flush losing token data; user pasted a truncated token.","solutions":["Request a new email-code-login email to get a fresh token.","Ensure the full token string is submitted without truncation or whitespace.","Check Redis connectivity and that the token TTL for 'email_code_login' tokens is sane.","If recurring, verify TokenManager configuration and that tokens are written under the correct type key."],"exampleFix":"// before\nfetch('/console/api/email-code-login/validity', {body: JSON.stringify({email, code, token})})\n// after - guard empty/expired token client-side\nif (!token || token.length < EXPECTED_LEN) {\n  setError('Your login link is incomplete or expired. Request a new code.')\n  return\n}\nfetch('/console/api/email-code-login/validity', {body: JSON.stringify({email, code, token})})","handlingStrategy":"try-catch","validationCode":"# Client-side: refuse to submit if the token looks malformed or stale\nif not token or len(token) < MIN_TOKEN_LEN:\n    prompt('Login link is invalid; request a new code.')\n    return\nsubmit_validity(email, code, token)","typeGuard":"null","tryCatchPattern":"from controllers.console.auth.error import InvalidTokenError\ntry:\n    verify_code(email, code, token)\nexcept InvalidTokenError:\n    prompt('Your login link expired; request a new code.')","preventionTips":["Drive the user to complete email-code login promptly after the code is sent.","Do not persist tokens across long-lived sessions.","Ensure Redis has enough memory and eviction policy that does not prematurely drop active tokens."],"tags":["auth","email-code-login","token","redis","expiry"],"backgroundTag":null,"analyzedSha":"ef8544b173fd6cd7a8e71df2cab576e52bebbfbc","analyzedAt":"2026-08-12T05:15:17.394Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}