BerriAI/litellm · error · ValueError
Unexpected response type
Error message
Unexpected response type: {type(raw_response)} What it means
LiteLLMResponsesTransformationHandler.transform_response only accepts raw_response values that are ResponsesAPIResponse instances — the typed object the Responses API client returns. Anything else (a plain dict, ModelResponse, string, or None) violates the transformation contract and is rejected before conversion to a chat completion. This is an internal API contract check, not user input validation.
Solutions
- Construct a ResponsesAPIResponse(**dict_payload) before calling transform_response
- Prefer the public litellm.completion path where typing is handled end to end
- Add an isinstance assert in custom adapters so wrong types fail loudly at the boundary
- Upgrade litellm if a provider adapter regression caused it
Example fix
// before from litellm.types.llms.openai import ResponsesAPIResponse model_response = transform_handler.transform_response(model, raw_response=data_dict, ...) # ValueError: Unexpected response type // after raw = ResponsesAPIResponse(**data_dict) model_response = transform_handler.transform_response(model, raw_response=raw, ...)
Defensive patterns
Strategy: type-guard
Validate before calling
from litellm.types.llms.openai import ResponsesAPIResponse
def ensure_responses_api_response(raw):
if isinstance(raw, dict):
return ResponsesAPIResponse(**raw)
return raw Type guard
from litellm.types.llms.openai import ResponsesAPIResponse
def is_responses_api_response(obj) -> bool:
return isinstance(obj, ResponsesAPIResponse) Try / catch
try:
model_response = handler.transform_response(model, raw_response=raw, ...)
except ValueError as e:
if "Unexpected response type" in str(e):
raise TypeError(f"adapter must pass ResponsesAPIResponse, got {type(raw)}") from e
raise Prevention
- Wrap raw dicts in ResponsesAPIResponse at your adapter boundary before transform_response
- Add isinstance asserts in custom provider adapters so wrong types fail at the seam
- Keep litellm subpackages on one version
When it happens
Trigger: Calling transform_response directly with a dict parsed from JSON instead of a ResponsesAPIResponse; a custom provider transformation returning the wrong type; version skew where a provider adapter changed its return type; middleware swapping the response object.
Common situations: Developers wiring custom providers into the responses bridge; code that re-implements parts of litellm's pipeline; partial upgrades where litellm subpackages disagree on types; tests passing fixtures of the wrong type.
Related errors
- Stream completed response is invalid
- Chat provider: Invalid chunk type
- model is required
- api base needs to be a string. api_base=
- api_base not set for Hosted VLLM responses API. Set via…
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/bb6a5ca96d60c7de.
Report an issue: GitHub.
Appendix: source
Thrown at litellm/completion_extras/litellm_responses_transformation/transformation.py:744
self,
model: str,
raw_response: "BaseModel",
model_response: "ModelResponse",
logging_obj: "LiteLLMLoggingObj",
request_data: dict,
messages: list["AllMessageValues"],
optional_params: dict,
litellm_params: dict,
encoding: object,
api_key: str | None = None,
json_mode: bool | None = None,
) -> "ModelResponse":
"""Transform Responses API response to chat completion response"""
from litellm.responses.utils import ResponseAPILoggingUtils
from litellm.types.llms.openai import ResponsesAPIResponse
if not isinstance(raw_response, ResponsesAPIResponse):
raise ValueError(f"Unexpected response type: {type(raw_response)}")
if raw_response.error is not None:
raise ValueError(f"Error in response: {raw_response.error}")
output_items = raw_response.output
if len(output_items) == 0:
recovered_output_items: Final = self._recover_output_items_from_logging(logging_obj)
if recovered_output_items:
output_items = cast(Any, recovered_output_items)
raw_response.output = cast(Any, recovered_output_items)
verbose_logger.warning(
"Recovered empty Responses API output from raw SSE for model=%s",
model,
)
# Convert response output to choices using the static helper
choices: Final = self._convert_response_output_to_choices(
output_items=output_items,View on GitHub (pinned to 77b7c6c40c)