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
- 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.
- Exempt api.search.brave.com from TLS inspection or proxy rewriting, or route around the proxy.
- Retry the call in your own code — truncated bodies are frequently transient.
- 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
- Exempt api.search.brave.com from TLS-inspecting proxies that rewrite bodies.
- Validate the response Content-Type is application/json when calling HTTP APIs yourself.
- Log resp text on JSON parse failures — an HTML block page from a proxy is the most common root cause.
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
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Brave Search API connection failed: {exc}
- Invalid JSON response from {oauth_endpoint}
- Brave Search API request timed out after {self._timeout}s: {
- Error. A valid pyproject.toml file is required. Check that a
- Project name '{name}' would generate invalid Python module n
AI-assisted analysis of crewAIInc/crewAI@754d7323be (2026-08-15).
Data as JSON: /api/errors/060c65e60016ee19.
Report an issue: GitHub.