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

  1. curl the exact inspection URL from the proxy host and inspect Content-Type — it must be application/json.
  2. Correct CISCO_AI_DEFENSE_API_BASE to the full API endpoint (or unset it to use CISCO_DEFAULT_API_BASE).
  3. Bypass HTML-injecting middleboxes for the Cisco domain, or fix the proxy policy.
  4. 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

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


AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18). Data as JSON: /api/errors/aa31c3ec31868a30. Report an issue: GitHub.