Billionmail/BillionMail · warning
Invalid validation code
Error message
Invalid validation code
What it means
After confirming the captcha fields are present, Login verifies them via service.VerifyCaptcha. If the verification service rejects the ID/code pair, this error is returned and login is aborted (validateSuccess stays false).
Source
Thrown at core/internal/controller/rbac/rbac_v1_auth.go:74
releaseTime = time.Now().Unix() + int64(blockTime)
public.SetCache(k, releaseTime, blockTime)
}
err = fmt.Errorf("Login failed too many times, please try again after %d seconds", releaseTime-time.Now().Unix())
return
}
// Check if validation code is required
if mustValidateCode {
validateSuccess = false
if req.ValidateCodeId == "" || req.ValidateCode == "" {
err = fmt.Errorf("Validation code ID and code cannot be empty")
return
}
if !service.VerifyCaptcha(req.ValidateCodeId, req.ValidateCode) {
err = fmt.Errorf("Invalid validation code")
return
}
validateSuccess = true
}
// Verify username and password
account, err := service.Account().Login(ctx, req.Username, req.Password)
if err != nil {
err = fmt.Errorf("Invalid username or password")
return
}
// Get account roles
roles, err := service.Account().GetAccountRoles(ctx, account.AccountId)
if err != nil {
err = fmt.Errorf("Failed to get account roles")
returnView on GitHub (pinned to fc36c76c05)
Solutions
- Re-request a fresh captcha and retry with the new code
- Ensure the captcha store (Redis/session) is shared across all app instances
- Submit each captcha exactly once — regenerate after any failed attempt
- Check captcha TTL and make the UI refresh the image before expiry
Example fix
// before
// retrying login with the same old captcha
await api.login({ ...body, validate_code: sameCode })
// after
const fresh = await api.getCaptcha();
await api.login({ ...body, validate_code_id: fresh.id, validate_code: newUserInput }) Defensive patterns
Strategy: retry
Validate before calling
// captcha is single-use and time-limited; refresh it if older than ~2 min if (captchaFetchedAt && Date.now() - captchaFetchedAt > 120_000) captcha = await api.getCaptcha();
Try / catch
try {
await api.login(body);
} catch (err) {
if (String(err.message).includes('Invalid validation code')) {
const fresh = await api.getCaptcha();
// re-prompt user and retry once with the new code
} else throw err;
} Prevention
- Never reuse a captcha ID/code across attempts
- Refresh the captcha before its TTL expires
- Ensure all replicas share the same captcha store (Redis)
- Handle case sensitivity in code entry or make codes case-insensitive server-side
When it happens
Trigger: User types the captcha wrong; the captcha has expired (one-time token already used or TTL elapsed); ValidateCodeId reused across attempts; captcha backend (session/redis) lost state so the stored answer is gone; distributed deployment where verification hits a different Redis than generation.
Common situations: Expired captcha left on screen while the user took too long; double-submit sending the same captcha twice; multiple app replicas without a shared captcha store; typos in case-sensitive codes.
Related errors
- Validation code ID and code cannot be empty
- Login failed too many times, please try again after %d secon
- Invalid username or password
- failed to get validate code: %w
- API key validation failed: %w
AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05).
Data as JSON: /api/errors/363882079ce4bd96.
Report an issue: GitHub.