{"record":{"id":"6cc2c2d11e2805cb","repo":"knadh/listmonk","slug":"hcaptcha-failed-s","errorCode":null,"errorMessage":"hCaptcha failed: %s","messagePattern":"hCaptcha failed: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/captcha/captcha.go","lineNumber":180,"sourceCode":"\t\t\"response\": {token},\n\t})\n\tif err != nil {\n\t\treturn err, false\n\t}\n\n\tdefer resp.Body.Close()\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn err, false\n\t}\n\n\tvar r hCaptchaResp\n\tif err := json.Unmarshal(body, &r); err != nil {\n\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 {","sourceCodeStart":162,"sourceCodeEnd":198,"githubUrl":"https://github.com/knadh/listmonk/blob/670c01717d48647093335cc23a6be6f4b79c3b6b/internal/captcha/captcha.go#L162-L198","documentation":"verifyHCaptcha returns this when the hCaptcha siteverify API responds with success=false. The response's error-codes array is joined into the message, explaining why hCaptcha rejected the token (bad secret, expired/already-used token, missing-input, etc.). This is the normal rejection path for failed human verification, not an infrastructure error.","triggerScenarios":"Calling Verify with an hCaptcha token when hCaptcha's siteverify endpoint replies {\"success\": false, \"error-codes\": [...]}, e.g. the user failed the challenge, the token was already redeemed, the response token expired (~2 minutes), or the secret key is wrong.","commonSituations":"User submits form twice so the token is reused; slow form submission exceeds token lifetime; wrong secret key between environments (staging vs production); bots failing the challenge — the most common production occurrence.","solutions":["Read the joined error-codes in the message: 'invalid-input-secret' means fix the secret key; 'timeout-or-duplicate' means the token expired or was reused.","For timeout-or-duplicate, ensure the token is submitted once — reset the hCaptcha widget and fetch a fresh token after each attempt.","Verify the secret key matches the sitekey's environment (hCaptcha dashboard).","Return a user-facing 'captcha failed, please retry' message and never treat this as a 5xx server error.","Confirm the form actually posts the h-captcha-response field as the token."],"exampleFix":"// before: generic rejection message\nreturn fmt.Errorf(\"hCaptcha failed: %s\", strings.Join(r.ErrorCodes, \",\")), false\n// after: map common codes to actionable messages\nswitch {\ncase contains(r.ErrorCodes, \"invalid-input-secret\"):\n  return errors.New(\"hcaptcha secret key is invalid\"), false\ncase contains(r.ErrorCodes, \"timeout-or-duplicate\"):\n  return errors.New(\"captcha token expired or reused, retry\"), false\n}","handlingStrategy":"try-catch","validationCode":"token := r.PostFormValue(\"h-captcha-response\")\nif token == \"\" {\n  return errors.New(\"hCaptcha token missing from form submission\")\n}\n// optionally check token shape before calling siteverify\nif len(token) < 20 {\n  return errors.New(\"hCaptcha token malformed\")\n}","typeGuard":"func hCaptchaTokenPresent(r *http.Request) bool {\n  return r.PostFormValue(\"h-captcha-response\") != \"\"\n}","tryCatchPattern":"if err, ok := captcha.Verify(token); !ok {\n  if err != nil && strings.HasPrefix(err.Error(), \"hCaptcha failed:\") {\n    switch {\n    case strings.Contains(err.Error(), \"timeout-or-duplicate\"):\n      // user retry: reset widget, new token\n    case strings.Contains(err.Error(), \"invalid-input-secret\"):\n      log.Error(\"wrong hCaptcha secret key — fix config\")\n    default:\n      // likely a bot: reject normally\n    }\n    http.Error(w, \"captcha failed, please retry\", http.StatusBadRequest)\n    return\n  }\n}","preventionTips":["Reset the hCaptcha widget after every failed submission so a fresh token is generated","Use distinct sitekey/secret pairs per environment and verify them in deploy checks","Expect timeout-or-duplicate on double submits — handle it as a normal user retry","Never retry automatically with the same token; siteverify accepts each token once"],"tags":["go","captcha","hcaptcha","verification","anti-bot"],"backgroundTag":"captcha-verification-failed","analyzedSha":"670c01717d48647093335cc23a6be6f4b79c3b6b","analyzedAt":"2026-09-01T03:39:35.452Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}