run-llama/llama_index · error · ValueError

At least one of requests_per_minute or tokens_per_minute mus

Error message

At least one of requests_per_minute or tokens_per_minute must be set.

What it means

SlidingWindowRateLimiter is a pydantic model whose model_validator(mode='after') enforces that at least one limit is configured: requests_per_minute or tokens_per_minute. Creating one with both None raises ValueError, since a limiter with no cap is meaningless.

Source

Thrown at llama-index-core/llama_index/core/rate_limiter.py:291

        gt=0,
    )
    token_burst: float = Field(
        default=0.0,
        ge=0.0,
        description=(
            "Additional tokens allowed as burst capacity within the sliding window. "
            "Set to 0 for a strict cap."
        ),
    )

    _request_timestamps: Deque[float] = PrivateAttr(default_factory=deque)
    _token_usage: Deque[Tuple[float, float]] = PrivateAttr(default_factory=deque)
    _lock: threading.Lock = PrivateAttr(default_factory=threading.Lock)

    @model_validator(mode="after")
    def _check_limits(self) -> "SlidingWindowRateLimiter":
        if self.requests_per_minute is None and self.tokens_per_minute is None:
            raise ValueError(
                "At least one of requests_per_minute or tokens_per_minute must be set."
            )
        return self

    def _prune_request_timestamps(self, now: float) -> None:
        """Remove request timestamps outside the sliding window. Hold _lock."""
        while (
            self._request_timestamps
            and self._request_timestamps[0] < now - _SLIDING_WINDOW_SECONDS
        ):
            self._request_timestamps.popleft()

    def _prune_token_usage(self, now: float) -> None:
        """Remove token usage entries outside the sliding window. Hold _lock."""
        while (
            self._token_usage
            and self._token_usage[0][0] < now - _SLIDING_WINDOW_SECONDS
        ):

View on GitHub (pinned to afd0fef371)

Solutions

  1. Set at least one limit, e.g. SlidingWindowRateLimiter(requests_per_minute=60)
  2. When loading from config, apply defaults for missing values before construction: rpm or 60, tpm or 100_000
  3. Validate configuration early (fail fast at startup) rather than deep inside a query run

Example fix

// before
rpm = os.getenv("RATE_LIMIT_RPM")  # may be None
tpm = os.getenv("RATE_LIMIT_TPM")  # may be None
limiter = SlidingWindowRateLimiter(requests_per_minute=rpm, tokens_per_minute=tpm)

// after
rpm = os.getenv("RATE_LIMIT_RPM")
tpm = os.getenv("RATE_LIMIT_TPM")
if rpm is None and tpm is None:
    raise SystemExit("Configure RATE_LIMIT_RPM or RATE_LIMIT_TPM")
limiter = SlidingWindowRateLimiter(
    requests_per_minute=float(rpm) if rpm else None,
    tokens_per_minute=float(tpm) if tpm else None,
)
Defensive patterns

Strategy: validation

Validate before calling

def build_limiter(rpm, tpm):
    if rpm is None and tpm is None:
        raise ValueError("Configure RATE_LIMIT_RPM or RATE_LIMIT_TPM before startup")
    return SlidingWindowRateLimiter(
        requests_per_minute=float(rpm) if rpm is not None else None,
        tokens_per_minute=float(tpm) if tpm is not None else None,
    )

Prevention

When it happens

Trigger: SlidingWindowRateLimiter() with no arguments, or explicitly passing requests_per_minute=None, tokens_per_minute=None (e.g. forwarding unset config values from env/CLI where missing settings become None).

Common situations: Building the limiter from optional config (os.getenv returning None) and passing both unset values through; instantiating to inspect defaults before deciding limits.

Related errors


AI-assisted analysis of run-llama/llama_index@afd0fef371 (2026-08-15). Data as JSON: /api/errors/e59ff82a3e995b9d. Report an issue: GitHub.