BerriAI/litellm · warning · Exception
Response from opik API status_code
Error message
Response from opik API status_code: {response.status_code}, text: {response.text} What it means
OpikLogger._sync_send POSTs a batch of span data to the Opik API and calls response.raise_for_status(), then additionally requires the response to be exactly 204 No Content — any other 2xx code raises Exception('Response from opik API status_code: ..., text: ...'). The outer except immediately catches it and records it via verbose_logger.exception('OpikLogger failed to send batch'), so it never propagates to the caller: it is an observability/log-line error, and the LLM request itself is unaffected.
Solutions
- Enable verbose/DEBUG logging and read the full logged exception — it includes the status code and response text from Opik, which identifies auth vs routing vs backend-version issues
- Verify OPIK_URL_OVERRIDE points at the correct Opik deployment base URL and that OPIK_API_KEY/OPIK_WORKSPACE_ID are valid for it
- Align versions: upgrade litellm (or pin an Opik backend version known compatible) so the batch endpoint returns the expected 204
- If the response text shows auth errors, rotate/re-set the Opik credentials in the deployment env
Example fix
# before OPIK_URL_OVERRIDE=https://opik.myco.com/wrong/path # after OPIK_URL_OVERRIDE=https://opik.myco.com # plus: pip install -U litellm to match the Opik API contract
Defensive patterns
Strategy: validation
Validate before calling
import os, httpx
base = os.getenv('OPIK_URL_OVERRIDE', 'https://www.comet.com/opik/api').rstrip('/')
key = os.environ.get('OPIK_API_KEY')
if key:
r = httpx.get(f'{base}/check-readiness', timeout=10) # cheap liveness/auth probe
if r.status_code >= 400:
raise SystemExit(f'Opik endpoint unhealthy or misconfigured: {r.status_code}') Try / catch
# The logger already swallows and logs this via verbose_logger.exception.
# Guard at the observability layer instead:
import logging
h = logging.getLogger('LiteLLM')
h.addHandler(AlertOnSubstringHandler(substring='OpikLogger failed to send batch')) Prevention
- Run a readiness probe against your Opik deployment before enabling the callback
- Pin litellm and your Opik backend to a tested-compatible version pair
- Alert on 'OpikLogger failed to send batch' log lines — each is silently dropped trace data
- Avoid response-rewriting proxies in front of Opik; pass the API through unchanged
When it happens
Trigger: A successful Opik HTTP response with an unexpected status — e.g. 200/201/202 returning a body where the client contract expects 204 — typically caused by an endpoint mismatch (OPIK_URL_OVERRIDE pointing at a different Opik version or an incompatible path), a gateway/proxy that rewrites responses, or non-2xx raise_for_status failures logged through the same handler. Visible in verbose logs after LLM calls when Opik tracing is enabled.
Common situations: Self-hosted Opik (Comet) whose version returns a JSON body with 200 on batch ingest; reverse proxies (nginx, service meshes) injecting responses; OPIK_URL_OVERRIDE typos hitting the wrong route; version drift between litellm's Opik client expectations and the deployed Opik backend; workspace/API-key misconfigurations that produce error bodies instead of 204.
Related errors
- DeepEval logging error
- ARIZE_API_KEY not found in environment variables
- Both project_id and topic_id must be provided
- DataDogLLMObs: Unexpected response - status_code
- endpoint not set for GenericAPILogger…
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/0ead195875f930bf.
Report an issue: GitHub.
Appendix: source
Thrown at litellm/integrations/opik/opik.py:175
"OpikLogger added event to log_queue - Will flush in %s seconds...", self.flush_interval
)
if len(self.log_queue) >= self.batch_size:
verbose_logger.debug("OpikLogger - Flushing batch")
await self.flush_queue()
except Exception as e:
verbose_logger.exception("OpikLogger failed to log success event - %s\n%s", e, traceback.format_exc())
def _sync_send(self, url: str, headers: dict[str, str], batch: dict[str, Any]) -> None:
try:
response: Final = self.sync_httpx_client.post(
url=url,
headers=headers,
json=batch,
)
response.raise_for_status()
if response.status_code != 204:
raise Exception(f"Response from opik API status_code: {response.status_code}, text: {response.text}")
except Exception as e:
verbose_logger.exception("OpikLogger failed to send batch - %s\n%s", e, traceback.format_exc())
def log_success_event(
self,
kwargs: dict[str, Any],
response_obj: Any,
start_time: datetime,
end_time: datetime,
) -> None:
try:
if _should_skip_event(kwargs):
return
# Build payload using the payload builder
trace_payload, span_payload = opik_payload_builder.build_opik_payload(
kwargs=kwargs,
response_obj=response_obj,View on GitHub (pinned to 77b7c6c40c)