{"record":{"id":"78c812a2adafb90c","repo":"Billionmail/BillionMail","slug":"login-failed-too-many-times-please-try-again-afte","errorCode":null,"errorMessage":"Login failed too many times, please try again after %d seconds","messagePattern":"Login failed too many times, please try again after (.+?) seconds","errorType":"http","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"core/internal/controller/rbac/rbac_v1_auth.go","lineNumber":60,"sourceCode":"\t\t// Login success\n\t\tif loginSuccessFlag {\n\t\t\tpublic.RemoveCache(cacheKey)\n\t\t\treturn\n\t\t}\n\n\t\t// Increment login retries\n\t\tpublic.SetCache(cacheKey, loginRetries+1, 300)\n\t}()\n\n\tif loginRetries >= maxRetries {\n\t\tk := \"USER_LOGIN_RETRIES_RELEASE_TIME:\" + clientIp\n\t\treleaseTime, blocked := public.GetCache(k).(int64)\n\t\tif !blocked {\n\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","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/controller/rbac/rbac_v1_auth.go#L42-L78","documentation":"Login implements rate limiting: after too many failed attempts a cache key stores a release timestamp. When a login is attempted while blocked, the handler returns this error with the remaining seconds until the block expires. The seconds value is releaseTime minus now.","triggerScenarios":"Repeated failed login attempts (wrong password) exceeding the configured threshold; a user retrying rapidly while already blocked; automated scripts/bots hammering the login endpoint; a stale cache entry from earlier failures still counting against the user.","commonSituations":"User forgets password and keeps guessing; CI/monitoring scripts using outdated credentials; shared IPs (office NAT) where multiple users' failures accumulate on one key; password manager with stale credentials auto-submitting.","solutions":["Wait the reported number of seconds before retrying","Reset the password via the recovery flow instead of guessing","Clear/invalidate the rate-limit cache key (Redis) if the block is confirmed to be from a shared IP or attacker","Increase blockTime/threshold configuration if too aggressive for legitimate users"],"exampleFix":"// before (immediate retry)\nawait login(user, wrongPassword)\n// after (respect the block)\nconst waitSec = 60; // from error message\nawait new Promise(r => setTimeout(r, waitSec * 1000));\nawait login(user, password)","handlingStrategy":"try-catch","validationCode":"// track local failure count and back off before hitting the limit\nif (++localFailures >= 4) await sleep(backoffMs);","typeGuard":null,"tryCatchPattern":"try {\n  await api.login(creds);\n} catch (err) {\n  const m = String(err.message).match(/try again after (\\d+) seconds/);\n  if (m) await sleep(parseInt(m[1], 10) * 1000 + 500); // retry after block expires\n  else throw err;\n}","preventionTips":["Verify credentials before resubmitting (password manager)","Stop guessing and use password reset after 2-3 failures","Avoid shared automation scripts with hardcoded credentials","Use exponential backoff on login failures"],"tags":["rate-limit","authentication","login"],"backgroundTag":"too-many-login-attempts","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"}