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

  1. Restart the registration flow at /email-register/email-send to obtain a fresh token.
  2. Ensure the client forwards the exact token string from the email link without trimming or URL-decoding twice.
  3. Check that the registration token TTL configured on the server is long enough for your email delivery latency.
  4. 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

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

Related errors


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