knadh/listmonk · error
captcha token already used
Error message
captcha token already used
What it means
This error is returned by verifyAltcha when a client submits an ALTCHA proof-of-work captcha token that has already been verified. The manager keeps a short-lived store (tmptokens) of previously used tokens; if Check() finds the token present, it means the exact challenge solution was already consumed and verified, so verification is refused to prevent replay attacks.
Source
Thrown at internal/captcha/captcha.go:199
}
return nil, true
}
// verifyAltcha verifies an Altcha response.
func (c *Captcha) verifyAltcha(payload string) (error, bool) {
valid, err := altcha.VerifySolution(payload, c.altcha.HMACKey, true)
if err != nil {
return fmt.Errorf("failed to verify captcha solution: %w", err), false
}
if !valid {
return fmt.Errorf("captcha verification failed"), false
}
// Disallow token reuse.
if _, err := tmptokens.Check(payload); err == nil {
return fmt.Errorf("captcha token already used"), false
}
tmptokens.Set(payload, 5*time.Minute, nil)
return nil, true
}
View on GitHub (pinned to 670c01717d)
Solutions
- Generate a fresh ALTCHA challenge and solve it for each submission instead of reusing a captured token
- Make the client-side submit handler idempotent: disable the button and do not resend on retry; re-solve the captcha for the retry
- Shorten frontend retry logic or re-request a challenge on 4xx responses before retrying
- If tokens expire too aggressively in tests, mint a new token per test case rather than sharing one
Example fix
// before await verify(oldPayload) // second call: 'captcha token already used' // after const challenge = await fetchNewChallenge() const payload = await solveAltcha(challenge) // fresh solve per submission await verify(payload)
Defensive patterns
Strategy: validation
Validate before calling
// Client-side: always solve a fresh challenge per submission
const challenge = await fetch('/api/captcha/challenge').then(r => r.json())
const payload = await solveAltcha(challenge) // never cache/reuse across requests Prevention
- Solve a new captcha challenge for every submission, including retries
- Disable the submit button after first click to prevent double-submits
- On network retry, treat the prior request as possibly-succeeded and re-solve the captcha rather than replaying the token
- Never share a verified token across multiple requests or test cases
When it happens
Trigger: A Verify call is made with a payload/token that was already successfully verified within the previous 5 minutes (the token's stored TTL). This happens on double-submits, form resubmissions, browser back-button resends, or a client retrying a request that actually succeeded the first time.
Common situations: Users double-clicking a submit button, frontend code retrying a failed HTTP request (timeouts) where the first request actually succeeded, automated tests reusing a captured token across multiple verification calls, or proxy/load-balancer request duplication.
Related errors
- failed to create Altcha challenge: %w
- failed to marshal Altcha challenge: %w
- failed to verify captcha solution: %w
- captcha verification failed
- error generating Altcha HMAC key: %v
AI-assisted analysis of knadh/listmonk@670c01717d (2026-09-01).
Data as JSON: /api/errors/b0f4ee90a6ae63cd.
Report an issue: GitHub.