zylon-ai/private-gpt · error · RateLimitExceeded

Brave Search API rate limit exceeded: {error_message}

Error message

Brave Search API rate limit exceeded: {error_message}

What it means

RateLimitExceeded raised when the Brave Search API responds HTTP 429. The provider extracts the error detail from the JSON body (response_data['error']) and raises this typed exception so callers can distinguish transient throttling from auth or quota problems. Logged at debug level before raising.

Source

Thrown at private_gpt/components/web/web_search/providers/brave.py:240

    def _check_response(
        self, response: ClientResponse, response_data: dict[str, Any]
    ) -> None:
        quota_header = response.headers.get("x-ratelimit-remaining")
        if quota_header is not None and quota_header.strip() == "0":
            raise QuotaConsumed(
                "Brave Search API quota exhausted (x-ratelimit-remaining=0)"
            )

        status_code = response.status
        if status_code < 400:
            return

        error = response_data.get("error", "Unknown error")
        error_message = self._extract_error_message(error)
        if status_code == 429:
            logger.debug(f"Brave Search API rate limit exceeded: {error_message}")
            raise RateLimitExceeded(
                f"Brave Search API rate limit exceeded: {error_message}"
            )
        elif status_code == 400:
            raise ValueError(f"Brave Search API invalid token ({error_message})")
        elif status_code >= 500:
            raise Exception(f"Brave Search API server error: {error_message}")
        else:
            raise Exception(f"Brave Search API error ({status_code}): {error_message}")

    def _build_request_params(
        self,
        query: str,
        num_links: int,
        offset: int,
        result_filter: str,
        safesearch: bool,
        freshness: str | None,
        spellcheck: bool,

View on GitHub (pinned to 4a030776a3)

Solutions

  1. Retry with exponential backoff honoring Retry-After if present.
  2. Increase brave.rate_limit (minimum seconds between requests) to match the plan's allowance.
  3. Reduce concurrency (web_search.num_concurrent_consumers) and enable web_search.cached.
  4. Upgrade the Brave plan if sustained traffic needs more throughput.

Example fix

# before
resp = await provider.make_query(q, n)

# after
for attempt in range(3):
    try:
        resp = await provider.make_query(q, n)
        break
    except RateLimitExceeded:
        await asyncio.sleep(2 ** attempt)
Defensive patterns

Strategy: retry

Type guard

def is_rate_limited(exc: BaseException) -> bool:
    return type(exc).__name__ == 'RateLimitExceeded'

Try / catch

from private_gpt.components.web.web_search.providers.brave import RateLimitExceeded

for delay in (1, 2, 4):
    try:
        results = await provider.make_query(q, num_links)
        break
    except RateLimitExceeded:
        await asyncio.sleep(delay)
else:
    raise

Prevention

When it happens

Trigger: Exceeding requests-per-second on the key; retry storms where a slow request is re-fired immediately; parallel consumers each holding their own Brave session bypassing the rate_limit spacing.

Common situations: Load tests against the search endpoint; multiple private-gpt replicas sharing one key without coordination; free-tier key under bursty chat traffic.

Related errors


AI-assisted analysis of zylon-ai/private-gpt@4a030776a3 (2026-08-15). Data as JSON: /api/errors/f22a10237ffc5129. Report an issue: GitHub.