BerriAI/litellm · error · CiscoAIDefenseGuardrailAPIError
Cisco AI Defense {surface} API returned a non-JSON response
Error message
Cisco AI Defense {surface} API returned a non-JSON response What it means
CiscoAIDefenseGuardrailAPIError raised when a Cisco AI Defense response with a 2xx status has a body that response.json() cannot parse (httpx raises ValueError). This means the endpoint answered but did not return JSON — typically an HTML error/interstitial page from a proxy, captive portal, or load balancer sitting in front of the real API.
Source
Thrown at litellm/proxy/guardrails/guardrail_hooks/cisco_ai_defense/cisco_ai_defense.py:843
body_snippet = ""
try:
body_snippet = exc.response.text[:500] if exc.response else ""
except Exception:
body_snippet = ""
raise CiscoAIDefenseGuardrailAPIError(
f"Cisco AI Defense {surface} API returned HTTP {status_code}: {body_snippet}"
) from exc
except httpx.TimeoutException as exc:
raise CiscoAIDefenseGuardrailAPIError(
f"Cisco AI Defense {surface} API call timed out after {self.timeout}s"
) from exc
except httpx.RequestError as exc:
raise CiscoAIDefenseGuardrailAPIError(f"Cisco AI Defense {surface} API request failed: {exc}") from exc
try:
return response.json()
except ValueError as exc:
raise CiscoAIDefenseGuardrailAPIError(
f"Cisco AI Defense {surface} API returned a non-JSON response"
) from exc
def _build_headers(self) -> dict[str, str]:
return {
CISCO_API_KEY_HEADER: self.api_key,
"Content-Type": "application/json",
"Accept": "application/json",
"User-Agent": f"litellm/{litellm_version}",
}
def _build_metadata(
self,
request_data: dict,
user_api_key_dict: UserAPIKeyAuth,
) -> dict[str, object]:
metadata: Final[dict[str, object]] = {}
View on GitHub (pinned to 77b7c6c40c)
Solutions
- curl the exact inspection URL from the proxy host and inspect Content-Type — it must be application/json.
- Correct CISCO_AI_DEFENSE_API_BASE to the full API endpoint (or unset it to use CISCO_DEFAULT_API_BASE).
- Bypass HTML-injecting middleboxes for the Cisco domain, or fix the proxy policy.
- Enable fallback_on_error: allow so a poisoned response path degrades to unscanned-but-logged instead of erroring.
Example fix
# before — base points at an HTML portal litellm_params: guardrail: cisco_ai_defense api_base: https://cisco-gw.internal.example.com # after — official endpoint litellm_params: guardrail: cisco_ai_defense # api_base omitted -> CISCO_DEFAULT_API_BASE
Defensive patterns
Strategy: validation
Validate before calling
import httpx, os
base = os.environ.get("CISCO_AI_DEFENSE_API_BASE")
if base:
r = httpx.get(base, timeout=5)
ctype = r.headers.get("content-type", "")
assert "json" in ctype or r.status_code in (401, 403, 404), (
f"api_base returns {ctype} status={r.status_code} — probably not the inspection API") Prevention
- Prefer the default Cisco API base; only override with a verified JSON endpoint.
- Content-Type smoke checks in deploy pipelines catch HTML-portal interception early.
- Exclude the Cisco API domain from HTML-injecting proxies (Zscaler-like) by policy.
When it happens
Trigger: CISCO_AI_DEFENSE_API_BASE pointing at a generic web server or auth portal that returns HTML 200; corporate proxies rewriting responses; a misrouted gateway that serves a landing page on the inspection path; empty bodies from some middleboxes.
Common situations: Custom api_base set to a domain whose root serves HTML; Zscaler/ Netsuite-style portals intercepting requests; api_base missing the API path prefix so the server returns its index page; rare occurrences when a CDN serves an error page with 200.
Related errors
- Error parsing BFL response: {e}
- Error parsing initial response: {e}
- Bedrock guardrail failed: {e}
- Cisco AI Defense API key is required. Set `CISCO_AI_DEFENSE_
- Cisco AI Defense {surface} API returned HTTP {status_code}:
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/aa31c3ec31868a30.
Report an issue: GitHub.