coder/code-server · warning · Error

Login rate limited!

Error message

Login rate limited!

What it means

The login POST handler (login.ts:78) calls limiter.canTry() and, if the caller has exceeded the allowed attempts within the window, throws the localized LOGIN_RATE_LIMIT message. This brute-force protection throttles repeated password guesses per IP.

Source

Thrown at src/node/routes/login.ts:78

  const to = (typeof req.query.to === "string" && req.query.to) || "/"
  if (await authenticated(req)) {
    return redirect(req, res, to, { to: undefined })
  }
  next()
})

router.get("/", async (req, res) => {
  res.send(await getRoot(req))
})

router.post<{}, string, { password?: string; base?: string } | undefined, { to?: string }>("/", async (req, res) => {
  const password = sanitizeString(req.body?.password)
  const hashedPasswordFromArgs = req.args["hashed-password"]

  try {
    // Check to see if they exceeded their login attempts
    if (!limiter.canTry()) {
      throw new Error(i18n.t("LOGIN_RATE_LIMIT") as string)
    }

    if (!password) {
      throw new Error(i18n.t("MISS_PASSWORD") as string)
    }

    const passwordMethod = getPasswordMethod(hashedPasswordFromArgs)
    const { isPasswordValid, hashedPassword } = await handlePasswordValidation({
      passwordMethod,
      hashedPasswordFromArgs,
      passwordFromRequestBody: password,
      passwordFromArgs: req.args.password,
    })

    if (isPasswordValid) {
      // The hash does not add any actual security but we do it for
      // obfuscation purposes (and as a side effect it handles escaping).
      res.cookie(req.cookieSessionName, hashedPassword, getCookieOptions(req))

View on GitHub (pinned to 51f90a376b)

Solutions

  1. Wait for the rate-limit window to elapse, then retry the login
  2. Verify the password is correct before retrying to avoid re-tripping the limiter
  3. For automated clients, space login attempts with backoff and cache the session cookie

Example fix

// before: tight retry loop
for (const pw of candidates) await tryLogin(pw)  // trips limiter

// after: respect 429/rate-limit with backoff
async function loginWithBackoff(pw) {
  for (let delay = 1000; ; delay *= 2) {
    const r = await tryLogin(pw)
    if (r.status === 429 || r.rateLimited) await sleep(delay)
    else return r
  }
}
Defensive patterns

Strategy: retry

Validate before calling

// Client-side: track attempts and back off before the server trips the limiter
const MAX_ATTEMPTS = 5
const WINDOW_MS = 60_000
let attempts = 0, windowStart = Date.now()
function canTryLogin(): boolean {
  if (Date.now() - windowStart > WINDOW_MS) { attempts = 0; windowStart = Date.now() }
  return attempts++ < MAX_ATTEMPTS
}
if (!canTryLogin()) throw new Error("Login rate limited; wait before retrying")

Try / catch

async function loginWithBackoff(password: string) {
  for (let delay = 2000; delay <= 60_000; delay *= 2) {
    try {
      return await tryLogin(password)
    } catch (e) {
      if (e instanceof Error && /rate limited/i.test(e.message)) {
        await new Promise((r) => setTimeout(r, delay))
        continue
      }
      throw e
    }
  }
  throw new Error("Exceeded login retries")
}

Prevention

When it happens

Trigger: Repeated POST /login attempts (correct or incorrect) from the same client/IP beyond the limiter's threshold within its time window; automation that retries logins in a tight loop.

Common situations: Users mistyping the password many times; CI smoke tests hammering /login; a shared NAT IP getting throttled because of another user's failures.

Related errors


AI-assisted analysis of coder/code-server@51f90a376b (2026-08-12). Data as JSON: /api/errors/e607e281eef1341a. Report an issue: GitHub.