Billionmail/BillionMail · warning

Login failed too many times, please try again after %d secon

Error message

Login failed too many times, please try again after %d seconds

What it means

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.

Source

Thrown at core/internal/controller/rbac/rbac_v1_auth.go:60

		// Login success
		if loginSuccessFlag {
			public.RemoveCache(cacheKey)
			return
		}

		// Increment login retries
		public.SetCache(cacheKey, loginRetries+1, 300)
	}()

	if loginRetries >= maxRetries {
		k := "USER_LOGIN_RETRIES_RELEASE_TIME:" + clientIp
		releaseTime, blocked := public.GetCache(k).(int64)
		if !blocked {
			releaseTime = time.Now().Unix() + int64(blockTime)
			public.SetCache(k, releaseTime, blockTime)
		}

		err = fmt.Errorf("Login failed too many times, please try again after %d seconds", releaseTime-time.Now().Unix())
		return
	}

	// Check if validation code is required
	if mustValidateCode {
		validateSuccess = false

		if req.ValidateCodeId == "" || req.ValidateCode == "" {
			err = fmt.Errorf("Validation code ID and code cannot be empty")
			return
		}

		if !service.VerifyCaptcha(req.ValidateCodeId, req.ValidateCode) {
			err = fmt.Errorf("Invalid validation code")
			return
		}

		validateSuccess = true

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Wait the reported number of seconds before retrying
  2. Reset the password via the recovery flow instead of guessing
  3. Clear/invalidate the rate-limit cache key (Redis) if the block is confirmed to be from a shared IP or attacker
  4. Increase blockTime/threshold configuration if too aggressive for legitimate users

Example fix

// before (immediate retry)
await login(user, wrongPassword)
// after (respect the block)
const waitSec = 60; // from error message
await new Promise(r => setTimeout(r, waitSec * 1000));
await login(user, password)
Defensive patterns

Strategy: try-catch

Validate before calling

// track local failure count and back off before hitting the limit
if (++localFailures >= 4) await sleep(backoffMs);

Try / catch

try {
  await api.login(creds);
} catch (err) {
  const m = String(err.message).match(/try again after (\d+) seconds/);
  if (m) await sleep(parseInt(m[1], 10) * 1000 + 500); // retry after block expires
  else throw err;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05). Data as JSON: /api/errors/78c812a2adafb90c. Report an issue: GitHub.