{"record":{"id":"9f35923269a0c631","repo":"knadh/listmonk","slug":"captcha-verification-failed","errorCode":null,"errorMessage":"captcha verification failed","messagePattern":"captcha verification failed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/captcha/captcha.go","lineNumber":194,"sourceCode":"\t\treturn err, true\n\t}\n\n\tif !r.Success {\n\t\treturn fmt.Errorf(\"hCaptcha failed: %s\", strings.Join(r.ErrorCodes, \",\")), false\n\t}\n\n\treturn nil, true\n}\n\n// verifyAltcha verifies an Altcha response.\nfunc (c *Captcha) verifyAltcha(payload string) (error, bool) {\n\tvalid, err := altcha.VerifySolution(payload, c.altcha.HMACKey, true)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to verify captcha solution: %w\", err), false\n\t}\n\n\tif !valid {\n\t\treturn fmt.Errorf(\"captcha verification failed\"), false\n\t}\n\n\t// Disallow token reuse.\n\tif _, err := tmptokens.Check(payload); err == nil {\n\t\treturn fmt.Errorf(\"captcha token already used\"), false\n\t}\n\ttmptokens.Set(payload, 5*time.Minute, nil)\n\n\treturn nil, true\n}\n","sourceCodeStart":176,"sourceCodeEnd":205,"githubUrl":"https://github.com/knadh/listmonk/blob/670c01717d48647093335cc23a6be6f4b79c3b6b/internal/captcha/captcha.go#L176-L205","documentation":"verifyAltcha returns this when altcha.VerifySolution successfully evaluates the payload but reports the proof-of-work solution is invalid — the client did not solve the challenge correctly, or the payload was signed with a different HMAC key than the server now holds. It is the expected rejection for non-solved or tampered Altcha payloads.","triggerScenarios":"Verify is called with a payload whose computed hash does not meet the challenge target, whose salt/algorithm fields were modified, or which was generated under an old HMAC key after a server-side key rotation.","commonSituations":"Bots submitting random payloads without solving proof-of-work (the dominant case); users bypassing the widget and posting raw form data; HMAC key changed on the server while clients still hold challenges from before the restart; complexity raised so slow clients time out and submit partial solutions.","solutions":["Treat it as a client error: return 400/re-serve the form with a fresh challenge and let the user retry.","Confirm the HMAC key hasn't changed since challenges were issued; if rotated, old in-flight challenges fail — restart form flows after rotation.","Log the payload's challenge/salt fields (not the payload itself) to distinguish tampering from key mismatch.","Ensure the client widget actually completed the proof-of-work before submission (check altcha 'verified' state)."],"exampleFix":"// before: key rotated in config without invalidating sessions\n// after: version the key and accept only current-generation challenges\nconst keyVersion = 2\nif challengeKeyVersion(payload) != keyVersion {\n  return errors.New(\"stale captcha challenge, reload the page\"), false\n}","handlingStrategy":"fallback","validationCode":"if err, ok := captcha.Verify(payload); !ok {\n  // re-issue a fresh challenge so the user can retry with new proof-of-work\n  challenge, err := captcha.GenerateChallenge(r.Context())\n  if err != nil {\n    http.Error(w, \"captcha unavailable\", http.StatusServiceUnavailable)\n    return\n  }\n  renderFormWithChallenge(w, challenge)\n  return\n}","typeGuard":null,"tryCatchPattern":"if err, ok := captcha.Verify(payload); !ok {\n  if err != nil && err.Error() == \"captcha verification failed\" {\n    // solution invalid: serve a new challenge and ask the user to retry\n    http.Error(w, \"captcha failed — reload and try again\", http.StatusBadRequest)\n    return\n  }\n}","preventionTips":["Always re-issue a fresh challenge after an invalid solution instead of reusing the old one","Announce and coordinate HMAC key rotations — old challenges fail after rotation","Rate-limit verification attempts to blunt proof-of-work-bypass bots","Ensure the client widget reports 'verified' state before enabling the submit button"],"tags":["go","captcha","altcha","proof-of-work","verification"],"backgroundTag":"captcha-verification-failed","analyzedSha":"670c01717d48647093335cc23a6be6f4b79c3b6b","analyzedAt":"2026-09-01T03:39:35.452Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}