langgenius/dify · error · EmailCodeError
email_code_error
email_code_error
Error message
Email code is invalid or expired.
What it means
Raised by EmailCodeError in ForgotPasswordCheckApi.post when req_data.code differs from the code stored in the reset token. Each failure increments add_forgot_password_error_rate_limit and can lead to error 412. The code is the pin delivered in the reset email.
Source
Thrown at api/controllers/console/auth/forgot_password.py:132
is_forgot_password_error_rate_limit = AccountService.is_forgot_password_error_rate_limit(user_email)
if is_forgot_password_error_rate_limit:
raise EmailPasswordResetLimitError()
token_data = AccountService.get_reset_password_data(req_data.token)
if token_data is None:
raise InvalidTokenError()
token_email = token_data.get("email")
if not isinstance(token_email, str):
raise InvalidEmailError()
normalized_token_email = token_email.lower()
if user_email != normalized_token_email:
raise InvalidEmailError()
if req_data.code != token_data.get("code"):
AccountService.add_forgot_password_error_rate_limit(user_email)
raise EmailCodeError()
# Verified, revoke the first token
AccountService.revoke_reset_password_token(req_data.token)
# Refresh token data by generating a new token
_, new_token = AccountService.generate_reset_password_token(
token_email, code=req_data.code, additional_data={"phase": "reset"}
)
AccountService.reset_forgot_password_error_rate_limit(user_email)
return {"is_valid": True, "email": normalized_token_email, "token": new_token}
@console_ns.route("/forgot-password/resets")
class ForgotPasswordResetApi(Resource):
@console_ns.doc("reset_password")
@console_ns.doc(description="Reset password with verification token")
@console_ns.expect(console_ns.models[ForgotPasswordResetPayload.__name__])View on GitHub (pinned to ef8544b173)
Solutions
- Trim whitespace and enter the code from the most recent reset email.
- Request a new reset email if uncertain, and use the new token+code pair together.
- Track attempts client-side and warn before the rate-limit threshold.
- Disable autofill on the code input to prevent stale-code substitution.
Example fix
// before
checkValidity({ email, token, code: rawInput });
// after
checkValidity({ email, token: latestToken, code: rawInput.trim() }); Defensive patterns
Strategy: validation
Validate before calling
const cleanCode = String(code).trim();
if (!/^\d{4,8}$/.test(cleanCode)) {
warnInvalidCodeFormat();
return;
} Type guard
function looksLikeResetCode(c) { return typeof c === 'string' && /^\d{4,8}$/.test(c.trim()); } Try / catch
try {
await checkValidity({ email, token, code: cleanCode });
} catch (e) {
if (e.code === 'email_code_error') bumpAttempts();
else if (e.code === 'email_password_reset_limit') showCooldown();
else throw e;
} Prevention
- Trim and validate the code format client-side.
- Use the code from the most recent reset email only.
- Disable autofill on the code input.
When it happens
Trigger: POST /console/api/forgot-password/validity with a wrong, stale, or typo'd reset code while the token and email are valid.
Common situations: User mistypes the reset code; code from an older reset email is used after a resend; autofill inserts a saved but outdated code; whitespace or formatting in the pasted code.
Related errors
- email_code_error
- email_password_reset_limit
- email_register_limit
- email_send_ip_limit
- invalid_or_expired_token
AI-assisted analysis of langgenius/dify@ef8544b173 (2026-08-12).
Data as JSON: /api/errors/0358c85ba7c6f0f0.
Report an issue: GitHub.