bytedance/deer-flow · warning · HTTPException

Too many login attempts. Try again later.

Error message

Too many login attempts. Try again later.

What it means

429 from the login route's in-process rate limiter: after _MAX_LOGIN_ATTEMPTS consecutive failures for a client IP, a lockout window is set and every further attempt during that window raises immediately, before credential verification. The lockout clears when the window expires (the record is deleted on the next post-lockout check).

Source

Thrown at backend/app/gateway/routers/auth.py:246

                real_ip = request.headers.get("x-real-ip", "").strip()
                if real_ip:
                    return real_ip
        except ValueError:
            # peer_host wasn't a parseable IP (e.g. "unknown") — fall through
            pass

    return peer_host or "unknown"


def _check_rate_limit(ip: str) -> None:
    """Raise 429 if the IP is currently locked out."""
    record = _login_attempts.get(ip)
    if record is None:
        return
    fail_count, lock_until = record
    if fail_count >= _MAX_LOGIN_ATTEMPTS:
        if time.time() < lock_until:
            raise HTTPException(
                status_code=429,
                detail="Too many login attempts. Try again later.",
            )
        del _login_attempts[ip]


_MAX_TRACKED_IPS = 10000


def _record_login_failure(ip: str) -> None:
    """Record a failed login attempt for the given IP."""
    # Evict expired lockouts when dict grows too large
    if len(_login_attempts) >= _MAX_TRACKED_IPS:
        now = time.time()
        expired = [k for k, (c, t) in _login_attempts.items() if c >= _MAX_LOGIN_ATTEMPTS and now >= t]
        for k in expired:
            del _login_attempts[k]
        # If still too large, evict cheapest-to-lose half: below-threshold

View on GitHub (pinned to 1dd6ba1acb)

Solutions

  1. Stop retrying and wait for the lockout window to elapse, then retry with verified credentials
  2. Fix the underlying credential failure (reset password) before the next attempt
  3. In tests, reset state by restarting the Gateway or use distinct client IPs / successful logins to clear records
  4. Ensure the proxy in front of the Gateway forwards the correct client IP so lockouts are per-user, not global

Example fix

# before
for pw in candidates:
    try_login(email, pw)  # hammers endpoint, triggers 429

# after
resp = try_login(email, first_candidate)
if resp.status_code == 429:
    backoff(resp.retry_after or 60)  # wait out lockout before next attempt
Defensive patterns

Strategy: fallback

Try / catch

try { await login(email, pw); } catch (e) {
  if (e.status === 429) { notify('Locked out; wait before retrying'); scheduleRetry(afterLockoutWindow()); return; }
  throw e;
}

Prevention

When it happens

Trigger: Repeated failed POST /api/auth/login from the same client IP (as derived by _get_client_ip, e.g. X-Forwarded-For or peer address) within the lockout window; also triggered by CI test suites hammering login with bad credentials.

Common situations: Automated retries with a wrong password; tests running many login failures without unique IPs; all users behind one NAT/proxy sharing a single client IP so one bad actor locks everyone out; stale X-Forwarded-For trust making the IP key wrong.

Related errors


AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14). Data as JSON: /api/errors/eb97b845d0d84096. Report an issue: GitHub.