{"record":{"id":"a42feb5a69475a6e","repo":"BerriAI/litellm","slug":"failed-to-parse-braintrust-api-response-str-e","errorCode":null,"errorMessage":"Failed to parse Braintrust API response: {str(e)}","messagePattern":"Failed to parse Braintrust API response: (.+?)","errorType":"http","errorClass":"HTTPException","httpStatus":502,"severity":"error","filePath":"cookbook/litellm_proxy_server/braintrust_prompt_wrapper_server.py","lineNumber":221,"sourceCode":"    print(f\"braintrust_token: {braintrust_token}\")\n\n    try:\n        async with httpx.AsyncClient(timeout=30.0) as client:\n            response = await client.get(braintrust_url, headers=headers)\n            response.raise_for_status()\n            braintrust_data = response.json()\n    except httpx.HTTPStatusError as e:\n        raise HTTPException(\n            status_code=e.response.status_code,\n            detail=f\"Braintrust API error: {e.response.text}\",\n        )\n    except httpx.RequestError as e:\n        raise HTTPException(\n            status_code=502,\n            detail=f\"Failed to connect to Braintrust API: {str(e)}\",\n        )\n    except json.JSONDecodeError as e:\n        raise HTTPException(\n            status_code=502,\n            detail=f\"Failed to parse Braintrust API response: {str(e)}\",\n        )\n\n    print(f\"braintrust_data: {braintrust_data}\")\n    # Transform the response\n    try:\n        transformed_data = transform_braintrust_response(braintrust_data)\n        print(f\"transformed_data: {transformed_data}\")\n        return JSONResponse(content=transformed_data)\n    except Exception as e:\n        raise HTTPException(\n            status_code=500,\n            detail=f\"Failed to transform Braintrust response: {str(e)}\",\n        )\n\n\n@app.get(\"/health\")","sourceCodeStart":203,"sourceCodeEnd":239,"githubUrl":"https://github.com/BerriAI/litellm/blob/6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d/cookbook/litellm_proxy_server/braintrust_prompt_wrapper_server.py#L203-L239","documentation":"A guard inside OpenAI text-completion-to-chat response conversion: when converting a raw TextCompletionResponse into LiteLLM's ModelResponse format, the transform requires both a non-None response_object and model_response_object. If either is None (e.g. the caller passed no response dict, or an internal path failed to initialize the target ModelResponse), a ValueError with the generic message 'Error in response object format' is raised. It indicates a malformed or missing payload rather than an HTTP failure.","triggerScenarios":"Calling litellm.completion() on a text-completion model where the transformation layer receives a None response object (empty provider response body) or an uninitialized ModelResponse; typically after a 200 response whose body failed to deserialize into TextCompletionResponse.","commonSituations":"Custom api_base gateways returning empty 200 bodies; provider API changes altering response shape; mocking/streaming tests that pass None where a response object is expected; version mismatches between litellm and the openai SDK pydantic models.","solutions":["Log the raw HTTP response body (set LITELLM_LOG=DEBUG) to see what the provider actually returned.","If using a custom api_base/proxy, verify it returns an OpenAI-spec completions JSON body with a choices array.","Reproduce with litellm.text_completion() to confirm the raw path works, isolating the conversion layer.","Upgrade litellm to the latest patch release in case a provider response-schema change was fixed."],"exampleFix":"// not a caller-fixable error; capture the raw payload for diagnosis\n# before\nresp = litellm.completion(model=\"openai/gpt-3.5-turbo-instruct\", messages=[...])\n\n# after\nimport litellm, logging\nlitellm.suppress_debug_info = True\nlogging.basicConfig(level=logging.DEBUG)\nresp = litellm.completion(model=\"openai/gpt-3.5-turbo-instruct\", messages=[...])  # inspect raw body in debug logs","handlingStrategy":"validation","validationCode":"def is_valid_text_completion_body(body: dict) -> bool:\n    return isinstance(body, dict) and isinstance(body.get(\"choices\"), list) and len(body[\"choices\"]) > 0","typeGuard":null,"tryCatchPattern":"try:\n    resp = litellm.completion(model=\"openai/gpt-3.5-turbo-instruct\", messages=[...])\nexcept ValueError as e:\n    if \"Error in response object format\" in str(e):\n        logger.error(\"provider returned malformed/empty body; capture raw response with LITELLM_LOG=DEBUG\")\n    raise","preventionTips":["Run with LITELLM_LOG=DEBUG when integrating new custom api_base endpoints.","Verify custom gateways return OpenAI-spec bodies with a non-empty choices array before adopting them.","Keep litellm updated; response-schema fixes land frequently."],"tags":["openai","response-parsing","text-completion","litellm","internal"],"backgroundTag":null,"analyzedSha":"6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d","analyzedAt":"2026-08-15T07:12:03.035Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}