{"record":{"id":"363882079ce4bd96","repo":"Billionmail/BillionMail","slug":"invalid-validation-code","errorCode":null,"errorMessage":"Invalid validation code","messagePattern":"Invalid validation code","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"core/internal/controller/rbac/rbac_v1_auth.go","lineNumber":74,"sourceCode":"\t\t\treleaseTime = time.Now().Unix() + int64(blockTime)\n\t\t\tpublic.SetCache(k, releaseTime, blockTime)\n\t\t}\n\n\t\terr = fmt.Errorf(\"Login failed too many times, please try again after %d seconds\", releaseTime-time.Now().Unix())\n\t\treturn\n\t}\n\n\t// Check if validation code is required\n\tif mustValidateCode {\n\t\tvalidateSuccess = false\n\n\t\tif req.ValidateCodeId == \"\" || req.ValidateCode == \"\" {\n\t\t\terr = fmt.Errorf(\"Validation code ID and code cannot be empty\")\n\t\t\treturn\n\t\t}\n\n\t\tif !service.VerifyCaptcha(req.ValidateCodeId, req.ValidateCode) {\n\t\t\terr = fmt.Errorf(\"Invalid validation code\")\n\t\t\treturn\n\t\t}\n\n\t\tvalidateSuccess = true\n\t}\n\n\t// Verify username and password\n\taccount, err := service.Account().Login(ctx, req.Username, req.Password)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"Invalid username or password\")\n\t\treturn\n\t}\n\n\t// Get account roles\n\troles, err := service.Account().GetAccountRoles(ctx, account.AccountId)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"Failed to get account roles\")\n\t\treturn","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/controller/rbac/rbac_v1_auth.go#L56-L92","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\n// retrying login with the same old captcha\nawait api.login({ ...body, validate_code: sameCode })\n// after\nconst fresh = await api.getCaptcha();\nawait api.login({ ...body, validate_code_id: fresh.id, validate_code: newUserInput })","handlingStrategy":"retry","validationCode":"// captcha is single-use and time-limited; refresh it if older than ~2 min\nif (captchaFetchedAt && Date.now() - captchaFetchedAt > 120_000) captcha = await api.getCaptcha();","typeGuard":null,"tryCatchPattern":"try {\n  await api.login(body);\n} catch (err) {\n  if (String(err.message).includes('Invalid validation code')) {\n    const fresh = await api.getCaptcha();\n    // re-prompt user and retry once with the new code\n  } else throw err;\n}","preventionTips":["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"],"tags":["captcha","authentication","verification"],"backgroundTag":"invalid-captcha","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}