BerriAI/litellm · error · Exception
{e.response.text}
Error message
{e.response.text} What it means
Exception raised in BraintrustLogger.log_success_event when the sync POST to /project_logs/{project_id}/insert returns an HTTP error status — the message is literally just the response body with no context about status code or URL. This is the final telemetry-insert step, so it fires after the project id was already resolved, meaning auth was once valid but now fails or the event payload is rejected.
Solutions
- Match the embedded response text to a status: re-POST a minimal event with curl using the same key to see the raw status code
- Restart the proxy after rotating BRAINTRUST_API_KEY so cached ids/clients are refreshed
- If payloads are huge, trim what gets logged or split the event
- For 429/5xx bodies, add retry with backoff around telemetry instead of failing the logging path
Defensive patterns
Strategy: try-catch
Validate before calling
import httpx, os
def can_insert_events(project_id: str) -> bool:
try:
r = httpx.post(
f"https://api.braintrust.dev/v1/project_logs/{project_id}/insert",
headers={"Authorization": f"Bearer {os.getenv('BRAINTRUST_API_KEY','')}"},
json={"events": []}, timeout=5)
return r.status_code < 400 or r.status_code == 400 # 400 on empty is fine; 401/404 is the signal
except httpx.RequestError:
return False Try / catch
try:
logger.log_success_event(kwargs, response_obj, start_time, end_time)
except Exception as e:
# message is the raw response body — map it: 401 => rotate key; 404 => stale project; 413 => trim payload
log_warning(f"braintrust telemetry dropped: {e}") # don't fail the user request Prevention
- Restart long-running proxies after credential rotation
- Truncate oversized prompts/completions before they reach the callback
- Never let telemetry exceptions propagate into user-facing request paths — catch and alert
When it happens
Trigger: A successful LLM call being logged when the Braintrust key expired or was revoked mid-process (401), the project_id is stale/deleted (404), the event JSON is malformed or exceeds size limits (400/413), or the org is rate limited (429).
Common situations: Keys rotated while long-running proxies stay up; very large logged payloads (big prompts/responses) tripping request-size limits; project deleted in the Braintrust UI while the proxy holds its cached id.
Understand the failure class
Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.
Related errors
- Failed to register project
- {e.response.text}
- Error listing files in
- Failed to fetch file
- Failed to fetch file
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/04ca7b67d9056695.
Report an issue: GitHub.
Appendix: source
Thrown at litellm/integrations/braintrust_logging.py:250
else:
request_data["output"] = output
if metrics is not None:
request_data["metrics"] = metrics
try:
print_verbose(
f"self.global_braintrust_sync_http_handler.post: {self.global_braintrust_sync_http_handler.post}"
)
self.global_braintrust_sync_http_handler.post(
url=f"{self.api_base}/project_logs/{project_id}/insert",
json={"events": [request_data]},
headers=self.headers,
)
if self.is_mock_mode:
print_verbose("[BRAINTRUST MOCK] Sync event successfully mocked")
except httpx.HTTPStatusError as e:
raise Exception(e.response.text)
except Exception as e:
raise e # don't use verbose_logger.exception, if exception is raised
async def async_log_success_event(self, kwargs, response_obj, start_time, end_time):
verbose_logger.debug("REACHES BRAINTRUST SUCCESS")
try:
litellm_call_id: Final = kwargs.get("litellm_call_id")
standard_logging_object: Final = kwargs.get("standard_logging_object", {})
prompt: Final = {"messages": kwargs.get("messages")}
output = None
choices = []
if response_obj is not None and (
kwargs.get("call_type", None) == "embedding" or isinstance(response_obj, litellm.EmbeddingResponse)
):
output = None
elif response_obj is not None and isinstance(response_obj, litellm.ModelResponse):
output = response_obj["choices"][0]["message"].json()
choices = response_obj["choices"]View on GitHub (pinned to 77b7c6c40c)