BerriAI/litellm · warning · Exception
Response from PostHog API status_code
Error message
Response from PostHog API status_code: {response.status_code}, text: {response.text} What it means
After POSTing a single event to {POSTHOG_API_URL}/batch/, the sync PostHog logger raises this if the final status is anything other than 200. Because response.raise_for_status() runs first, genuine 4xx/5xx surface as httpx.HTTPStatusError — this custom message only fires for statuses raise_for_status tolerates: 201/202/204-style codes and 3xx redirects (httpx does not follow redirects by default). The enclosing except logs it with verbose_logger.exception, so the telemetry event is dropped but the LLM call succeeds.
Solutions
- Set POSTHOG_API_URL to the exact region root with https (e.g. https://us.i.posthog.com or https://eu.i.posthog.com), no trailing path
- Reproduce with curl -X POST '{url}/batch/' -H 'Content-Type: application/json' -d '<sample batch>' and inspect status plus body
- Enable debug logging (set litellm verbose / LITELLM_LOG=DEBUG) to capture the exact status_code/text pair reported in the message
- If an intermediary returns non-200 success codes or redirects, exempt the PostHog host from rewriting or follow redirects yourself
Example fix
# before os.environ['POSTHOG_API_URL'] = 'http://app.posthog.com' # 301 -> non-200 -> exception in logs # after os.environ['POSTHOG_API_URL'] = 'https://us.i.posthog.com' # matches your project region
Defensive patterns
Strategy: validation
Validate before calling
import httpx, os
url = os.getenv('POSTHOG_API_URL', 'https://us.i.posthog.com').rstrip('/')
resp = httpx.post(f'{url}/batch/', json={'api_key': os.environ['POSTHOG_API_KEY'], 'batch': [], 'historical_migration': False})
if resp.status_code != 200:
raise RuntimeError(f'PostHog preflight failed: {resp.status_code} {resp.text}') Prevention
- Use the exact https region root for POSTHOG_API_URL to avoid 3xx redirects on the batch POST
- Run a batch-endpoint smoke test at startup before trusting telemetry
- Monitor verbose_logger output for dropped events — failures are non-blocking by design
When it happens
Trigger: POSTHOG_API_URL using http:// where the server answers the /batch/ POST with a 301/302 redirect to https; a self-hosted PostHog or an intermediary gateway returning 2xx-but-not-200 (201/202/204); the URL pointing at a non-PostHog service that replies with an odd success code.
Common situations: Region mismatch (us vs eu host), trailing path segments in POSTHOG_API_URL, corporate proxies rewriting responses, PostHog cloud incidents returning unusual statuses.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- POSTHOG_API_KEY is not set, set 'POSTHOG_API_KEY=<>'
- PostHog credentials not found in kwargs
- Response status code
- Blocked user check was never set. This call has no effect.
- DataDogLLMObs: Unexpected response - status_code
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/1a0c4ba88be5c19d.
Report an issue: GitHub.
Appendix: source
Thrown at litellm/integrations/posthog.py:102
raise Exception("PostHog credentials not found in kwargs")
event_payload: Final = self.create_posthog_event_payload(kwargs)
headers: Final = {
"Content-Type": "application/json",
}
payload: Final = self._create_posthog_payload([event_payload], api_key)
capture_url: Final = f"{api_url.rstrip('/')}/batch/"
response: Final = self.sync_client.post(
url=capture_url,
content=safe_dumps(payload),
headers=headers,
)
response.raise_for_status()
if response.status_code != 200:
raise Exception(f"Response from PostHog API status_code: {response.status_code}, text: {response.text}")
if self.is_mock_mode:
verbose_logger.debug("[POSTHOG MOCK] Sync event successfully mocked")
else:
verbose_logger.debug("PostHog: Sync event successfully sent")
except Exception as e:
verbose_logger.exception("PostHog Sync Layer Error - %s", e)
async def async_log_success_event(self, kwargs, response_obj, start_time, end_time):
try:
verbose_logger.debug("PostHog: Async logging - Enters logging function for model %s", kwargs)
self._ensure_async_setup() # Lazy initialization
await self._log_async_event(kwargs, response_obj, start_time, end_time)
except Exception as e:
verbose_logger.exception("PostHog Layer Error - %s", e)
async def async_log_failure_event(self, kwargs, response_obj, start_time, end_time):View on GitHub (pinned to 77b7c6c40c)