{"record":{"id":"958edd8c5cecd733","repo":"BerriAI/litellm","slug":"deepkeep-guardrail-api-failed-error","errorCode":null,"errorMessage":"DeepKeep guardrail API failed: {error}","messagePattern":"DeepKeep guardrail API failed: (.+?)","errorType":"exception","errorClass":"DeepKeepGuardrailAPIError","httpStatus":null,"severity":"error","filePath":"litellm/proxy/guardrails/guardrail_hooks/deepkeep/deepkeep.py","lineNumber":231,"sourceCode":"        self,\n        error: Exception,\n        inputs: GenericGuardrailAPIInputs,\n        input_type: Literal[\"request\", \"response\"],\n        logging_obj: Optional[\"LiteLLMLoggingObj\"],\n        is_unreachable: bool = True,\n    ) -> GenericGuardrailAPIInputs:\n        \"\"\"Handle errors from the DeepKeep API with fail-open/fail-closed logic.\"\"\"\n        if is_unreachable and self.unreachable_fallback == \"fail_open\":\n            http_status_code: Final = getattr(getattr(error, \"response\", None), \"status_code\", None)\n            return self._fail_open_passthrough(\n                inputs=inputs,\n                input_type=input_type,\n                logging_obj=logging_obj,\n                error=error,\n                **({\"http_status_code\": http_status_code} if http_status_code else {}),\n            )\n        verbose_proxy_logger.error(\"DeepKeep guardrail API error: %s\", str(error))\n        raise DeepKeepGuardrailAPIError(f\"DeepKeep guardrail API failed: {error}\")\n\n    @staticmethod\n    def _build_return_inputs(\n        *,\n        response_json: dict[str, Any],\n        texts: list,\n        images: Any | None,\n        tools: Any | None,\n        tool_calls: Any | None,\n        structured_messages: Any | None,\n    ) -> GenericGuardrailAPIInputs:\n        \"\"\"Merge original inputs with any guardrail-modified values from the API response.\n\n        Presence is checked with ``is not None`` (not truthiness) so that an\n        intentional empty-list replacement such as ``texts: []`` or\n        ``tool_calls: []`` is honoured and forwarded downstream rather than\n        silently discarded in favour of the original content.\n        \"\"\"","sourceCodeStart":213,"sourceCodeEnd":249,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/proxy/guardrails/guardrail_hooks/deepkeep/deepkeep.py#L213-L249","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Read the verbose proxy log line 'DeepKeep guardrail API error: <error>' to get the underlying status code and message.","Verify DEEPKEEP_API_KEY, DEEPKEEP_API_BASE, and firewall_id by curl-ing the firewall endpoint directly with the same X-API-Key header.","Set litellm_params: unreachable_fallback: fail_open in the guardrail config so 502/503/504 outages and timeouts pass traffic through instead of raising.","If the key is valid but a 404 persists, confirm the firewall is deployed in the DeepKeep console and the firewall_id matches.","Temporarily detach the deepkeep guardrail from guardrails-config to confirm the rest of the proxy is healthy."],"exampleFix":"# before (config.yaml)\nguardrails:\n  - guardrail_name: deepkeep\n    litellm_params:\n      guardrail: deepkeep\n      api_base: https://app.deepkeep.ai/firewall\n      unreachable_fallback: fail_closed\n\n# after\nguardrails:\n  - guardrail_name: deepkeep\n    litellm_params:\n      guardrail: deepkeep\n      api_base: https://app.deepkeep.ai/firewall\n      unreachable_fallback: fail_open","handlingStrategy":"fallback","validationCode":"import os\nmissing = [v for v in ('DEEPKEEP_API_KEY', 'DEEPKEEP_API_BASE') if not os.getenv(v)]\nif missing:\n    raise SystemExit(f'deploy blocked, missing env: {missing}')","typeGuard":"from litellm.proxy.guardrails.guardrail_hooks.deepkeep.deepkeep import DeepKeepGuardrailAPIError\n\ndef is_deepkeep_api_error(exc: BaseException) -> bool:\n    return isinstance(exc, DeepKeepGuardrailAPIError)","tryCatchPattern":"from litellm.proxy.guardrails.guardrail_hooks.deepkeep.deepkeep import DeepKeepGuardrailAPIError\ntry:\n    result = await guardrail.apply_guardrail(inputs, request_data, 'request', logging_obj)\nexcept DeepKeepGuardrailAPIError as e:\n    logger.warning('DeepKeep unreachable, failing open: %s', e)\n    result = dict(inputs)  # proceed unmoderated (or re-raise in strict mode)","preventionTips":["Configure unreachable_fallback: fail_open unless your compliance posture requires fail-closed.","Monitor the proxy log for 'DeepKeep guardrail API error' and alert on frequency spikes.","Smoke-test api_base, API key, and firewall_id after every credential rotation."],"tags":["deepkeep","guardrail","http-error","fail-closed","network"],"backgroundTag":"guardrail-api-request-failed","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}