langgenius/dify · error · InvalidTokenError
invalid_or_expired_token
invalid_or_expired_token
Error message
The token is invalid or has expired.
What it means
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.
Source
Thrown at api/controllers/console/auth/login.py:293
@console_ns.route("/email-code-login/validity")
class EmailCodeLoginApi(Resource):
@setup_required
@console_ns.expect(console_ns.models[EmailCodeLoginPayload.__name__])
@console_ns.response(200, "Success", console_ns.models[SimpleResultResponse.__name__])
@decrypt_code_field
@model_validate(EmailCodeLoginPayload)
def post(self, req_data: EmailCodeLoginPayload):
original_email = req_data.email
user_email = original_email.lower()
language = req_data.language
token_data = AccountService.get_email_code_login_data(req_data.token)
if token_data is None:
_log_console_login_failure(email=user_email, reason=LoginFailureReason.INVALID_EMAIL_CODE_TOKEN)
raise InvalidTokenError()
token_email = token_data.get("email")
normalized_token_email = token_email.lower() if isinstance(token_email, str) else token_email
if normalized_token_email != user_email:
_log_console_login_failure(email=user_email, reason=LoginFailureReason.EMAIL_CODE_EMAIL_MISMATCH)
raise InvalidEmailError()
if token_data["code"] != req_data.code:
_log_console_login_failure(email=user_email, reason=LoginFailureReason.INVALID_EMAIL_CODE)
raise EmailCodeError()
AccountService.revoke_email_code_login_token(req_data.token)
try:
account = _get_account_with_case_fallback(original_email)
except Unauthorized as exc:
_log_console_login_failure(email=user_email, reason=LoginFailureReason.ACCOUNT_BANNED)
raise AccountBannedError() from exc
except AccountRegisterError:View on GitHub (pinned to ef8544b173)
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.
Example fix
// before
fetch('/console/api/email-code-login/validity', {body: JSON.stringify({email, code, token})})
// after - guard empty/expired token client-side
if (!token || token.length < EXPECTED_LEN) {
setError('Your login link is incomplete or expired. Request a new code.')
return
}
fetch('/console/api/email-code-login/validity', {body: JSON.stringify({email, code, token})}) Defensive patterns
Strategy: try-catch
Validate before calling
# Client-side: refuse to submit if the token looks malformed or stale
if not token or len(token) < MIN_TOKEN_LEN:
prompt('Login link is invalid; request a new code.')
return
submit_validity(email, code, token) Type guard
null
Try / catch
from controllers.console.auth.error import InvalidTokenError
try:
verify_code(email, code, token)
except InvalidTokenError:
prompt('Your login link expired; request a new code.') Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
AI-assisted analysis of langgenius/dify@ef8544b173 (2026-08-12).
Data as JSON: /api/errors/21298d45f77004b1.
Report an issue: GitHub.