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 InvalidTokenError in EmailRegisterCheckApi.post when AccountService.get_email_register_data(req_data.token) returns None. The token is the JWT-style handle returned by the email-send step; None means it is missing, malformed, revoked, or past its TTL. This is the canonical 'your link is stale' signal during the two-phase register flow.
Source
Thrown at api/controllers/console/auth/email_register.py:130
@console_ns.route("/email-register/validity")
class EmailRegisterCheckApi(Resource):
@setup_required
@email_password_login_enabled
@email_register_enabled
@console_ns.expect(console_ns.models[EmailRegisterValidityPayload.__name__])
@console_ns.response(200, "Success", console_ns.models[VerificationTokenResponse.__name__])
@model_validate(EmailRegisterValidityPayload)
def post(self, req_data: EmailRegisterValidityPayload):
user_email = req_data.email.lower()
is_email_register_error_rate_limit = AccountService.is_email_register_error_rate_limit(user_email)
if is_email_register_error_rate_limit:
raise EmailRegisterLimitError()
token_data = AccountService.get_email_register_data(req_data.token)
if token_data is None:
raise InvalidTokenError()
token_email = token_data.get("email")
normalized_token_email = token_email.lower() if isinstance(token_email, str) else token_email
if user_email != normalized_token_email:
raise InvalidEmailError()
if req_data.code != token_data.get("code"):
AccountService.add_email_register_error_rate_limit(user_email)
raise EmailCodeError()
# Verified, revoke the first token
AccountService.revoke_email_register_token(req_data.token)
# Refresh token data by generating a new token
_, new_token = AccountService.generate_email_register_token(
user_email, code=req_data.code, additional_data={"phase": "register"}
)View on GitHub (pinned to ef8544b173)
Solutions
- Restart the registration flow at /email-register/email-send to obtain a fresh token.
- Ensure the client forwards the exact token string from the email link without trimming or URL-decoding twice.
- Check that the registration token TTL configured on the server is long enough for your email delivery latency.
- Verify the client is hitting the same environment (same Dify instance) that issued the token.
Example fix
// before: reuse a token stored days ago
checkValidity({ email, token: oldToken, code });
// after: detect expiry and re-issue
if (res.code === 'invalid_or_expired_token') {
const send = await emailSend({ email });
await checkValidity({ email, token: send.token, code: send.code });
} Defensive patterns
Strategy: try-catch
Validate before calling
// Sanity check before calling: ensure token is present and well-formed
if (!token || token.split('.').length < 2) {
await restartRegisterFlow(email);
return;
} Type guard
function isPlausibleToken(t) { return typeof t === 'string' && t.length > 16 && t.includes('.'); } Try / catch
try {
await checkValidity({ email, token, code });
} catch (e) {
if (e.code === 'invalid_or_expired_token') await restartRegisterFlow(email);
else throw e;
} Prevention
- Store the token in sessionStorage immediately after email-send and read from there.
- Complete the registration flow within the token TTL.
- Never reuse a token that was already consumed by a successful step.
When it happens
Trigger: POST /console/api/email-register/validity where req_data.token is unknown to the store, was already consumed by revoke_email_register_token, or has expired. Also fires if the client fabricates or truncates the token.
Common situations: User clicks an old registration link after the token TTL elapsed; user refreshed the registration page after the token was revoked; client lost the token from local state and substituted a placeholder; clock skew between issuer and verifier.
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/1456334441ae0f57.
Report an issue: GitHub.