crewAIInc/crewAI · error · RuntimeError

Brave Search API returned invalid JSON (HTTP {resp.status_co

Error message

Brave Search API returned invalid JSON (HTTP {resp.status_code}): {exc}

What it means

After a 200-series response, BraveSearchTool parses the body with resp.json(); if parsing raises ValueError (invalid or truncated JSON), it is converted to this RuntimeError including the HTTP status and parser error. This usually indicates a proxy or captive portal mangling the response, or a truncated body from a network fault — the request itself succeeded, so the tool does not retry.

Source

Thrown at lib/crewai-tools/src/crewai_tools/tools/brave_search_tool/base.py:218

            except requests.Timeout as exc:
                raise RuntimeError(
                    f"Brave Search API request timed out after {self._timeout}s: {exc}"
                ) from exc

            logger.debug(
                "Brave Search API request: %s %s -> %d",
                "GET",
                resp.url,
                resp.status_code,
            )

            # Response was OK, return the JSON body
            if resp.ok:
                try:
                    result: dict[str, Any] = resp.json()
                    return result
                except ValueError as exc:
                    raise RuntimeError(
                        f"Brave Search API returned invalid JSON (HTTP {resp.status_code}): {exc}"
                    ) from exc

            # Response was not OK, but is retryable
            # (e.g., 429 Too Many Requests, 500 Internal Server Error)
            if _is_retryable(resp) and attempt < _max_retries - 1:
                delay = _retry_delay(resp, attempt)
                logger.warning(
                    "Brave Search API returned %d. Retrying in %.1fs (attempt %d/%d)",
                    resp.status_code,
                    delay,
                    attempt + 1,
                    _max_retries,
                )
                time.sleep(delay)
                last_resp = resp
                continue

View on GitHub (pinned to 754d7323be)

Solutions

  1. Log resp-sized diagnostics: reproduce with `curl -s https://api.search.brave.com/... -H 'X-Subscription-Token: ...'` and inspect the raw body — HTML content points at a proxy/portal.
  2. Exempt api.search.brave.com from TLS inspection or proxy rewriting, or route around the proxy.
  3. Retry the call in your own code — truncated bodies are frequently transient.
  4. If the raw body is genuinely malformed JSON from Brave with no intermediary, capture it and report to Brave support.

Example fix

# before
results = tool._run("query")

# after
for attempt in range(3):
    try:
        results = tool._run("query")
        break
    except RuntimeError as e:
        if "invalid JSON" not in str(e) or attempt == 2:
            raise
Defensive patterns

Strategy: retry

Validate before calling

import requests

def returns_json(url: str, headers: dict) -> bool:
    r = requests.get(url, headers=headers, timeout=10)
    ct = r.headers.get("Content-Type", "")
    return "json" in ct.lower()

Try / catch

except RuntimeError as e:
    if "invalid JSON" in str(e):
        result = tool._run(query)  # truncated bodies are often transient; retry once
        if not isinstance(result, dict):
            raise
    else:
        raise

Prevention

When it happens

Trigger: Corporate proxy / TLS-inspection appliance rewriting the response into HTML; captive portal or error page returned with 200; truncated chunked response on flaky links; a Brave CDN edge serving a corrupted body.

Common situations: Zscaler/Netskope-style TLS interception injecting block pages; hotel/airport captive portals; mobile networks truncating responses; rare CDN corruption incidents.

Understand the failure class

Related errors


AI-assisted analysis of crewAIInc/crewAI@754d7323be (2026-08-15). Data as JSON: /api/errors/060c65e60016ee19. Report an issue: GitHub.