BerriAI/litellm · error · DeepKeepGuardrailAPIError

DeepKeep guardrail API failed: {error}

Error message

DeepKeep guardrail API failed: {error}

What it means

Raised by the DeepKeep guardrail hook when its POST to the DeepKeep AI Firewall fails and no fail-open path applies. In _handle_guardrail_request_error it wraps httpx Timeout, RequestError, and HTTPStatusError from raise_for_status() — i.e. any status other than 502/503/504, or those gateway statuses when unreachable_fallback is 'fail_closed' (Timeout always takes is_unreachable=True). The underlying exception text is embedded in the message, and the proxy log line 'DeepKeep guardrail API error: <error>' carries the detail.

Source

Thrown at litellm/proxy/guardrails/guardrail_hooks/deepkeep/deepkeep.py:231

        self,
        error: Exception,
        inputs: GenericGuardrailAPIInputs,
        input_type: Literal["request", "response"],
        logging_obj: Optional["LiteLLMLoggingObj"],
        is_unreachable: bool = True,
    ) -> GenericGuardrailAPIInputs:
        """Handle errors from the DeepKeep API with fail-open/fail-closed logic."""
        if is_unreachable and self.unreachable_fallback == "fail_open":
            http_status_code: Final = getattr(getattr(error, "response", None), "status_code", None)
            return self._fail_open_passthrough(
                inputs=inputs,
                input_type=input_type,
                logging_obj=logging_obj,
                error=error,
                **({"http_status_code": http_status_code} if http_status_code else {}),
            )
        verbose_proxy_logger.error("DeepKeep guardrail API error: %s", str(error))
        raise DeepKeepGuardrailAPIError(f"DeepKeep guardrail API failed: {error}")

    @staticmethod
    def _build_return_inputs(
        *,
        response_json: dict[str, Any],
        texts: list,
        images: Any | None,
        tools: Any | None,
        tool_calls: Any | None,
        structured_messages: Any | None,
    ) -> GenericGuardrailAPIInputs:
        """Merge original inputs with any guardrail-modified values from the API response.

        Presence is checked with ``is not None`` (not truthiness) so that an
        intentional empty-list replacement such as ``texts: []`` or
        ``tool_calls: []`` is honoured and forwarded downstream rather than
        silently discarded in favour of the original content.
        """

View on GitHub (pinned to 77b7c6c40c)

Solutions

  1. Read the verbose proxy log line 'DeepKeep guardrail API error: <error>' to get the underlying status code and message.
  2. Verify DEEPKEEP_API_KEY, DEEPKEEP_API_BASE, and firewall_id by curl-ing the firewall endpoint directly with the same X-API-Key header.
  3. Set litellm_params: unreachable_fallback: fail_open in the guardrail config so 502/503/504 outages and timeouts pass traffic through instead of raising.
  4. If the key is valid but a 404 persists, confirm the firewall is deployed in the DeepKeep console and the firewall_id matches.
  5. Temporarily detach the deepkeep guardrail from guardrails-config to confirm the rest of the proxy is healthy.

Example fix

# before (config.yaml)
guardrails:
  - guardrail_name: deepkeep
    litellm_params:
      guardrail: deepkeep
      api_base: https://app.deepkeep.ai/firewall
      unreachable_fallback: fail_closed

# after
guardrails:
  - guardrail_name: deepkeep
    litellm_params:
      guardrail: deepkeep
      api_base: https://app.deepkeep.ai/firewall
      unreachable_fallback: fail_open
Defensive patterns

Strategy: fallback

Validate before calling

import os
missing = [v for v in ('DEEPKEEP_API_KEY', 'DEEPKEEP_API_BASE') if not os.getenv(v)]
if missing:
    raise SystemExit(f'deploy blocked, missing env: {missing}')

Type guard

from litellm.proxy.guardrails.guardrail_hooks.deepkeep.deepkeep import DeepKeepGuardrailAPIError

def is_deepkeep_api_error(exc: BaseException) -> bool:
    return isinstance(exc, DeepKeepGuardrailAPIError)

Try / catch

from litellm.proxy.guardrails.guardrail_hooks.deepkeep.deepkeep import DeepKeepGuardrailAPIError
try:
    result = await guardrail.apply_guardrail(inputs, request_data, 'request', logging_obj)
except DeepKeepGuardrailAPIError as e:
    logger.warning('DeepKeep unreachable, failing open: %s', e)
    result = dict(inputs)  # proceed unmoderated (or re-raise in strict mode)

Prevention

When it happens

Trigger: DeepKeep returns 401/403 (invalid X-API-Key), 404 (wrong firewall_id or api_base path), 429, or 5xx while unreachable_fallback='fail_closed'; the firewall hostname fails DNS resolution; the request exceeds the httpx client timeout; the response body is not valid JSON after a 200.

Common situations: Expired or rotated DeepKeep API key; typo in DEEPKEEP_API_BASE; firewall_id that does not exist in the DeepKeep tenant; a DeepKeep outage with fail-open not configured, so every proxied LLM call fails while the guardrail endpoint is down.

Related errors


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