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

  1. Request a new email-code-login email to get a fresh token.
  2. Ensure the full token string is submitted without truncation or whitespace.
  3. Check Redis connectivity and that the token TTL for 'email_code_login' tokens is sane.
  4. 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

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

Related errors


AI-assisted analysis of langgenius/dify@ef8544b173 (2026-08-12). Data as JSON: /api/errors/21298d45f77004b1. Report an issue: GitHub.