BerriAI/litellm · error · ValueError

response must be of type OCRResponse got type={type(response

Error message

response must be of type OCRResponse got type={type(response)}

What it means

The OCR cost calculator only prices OCRResponse objects (litellm.llms.base_llm.ocr.transformation.OCRResponse). If response is None or any other type — e.g. a raw provider dict or a generic ModelResponse — it raises ValueError reporting the actual type.

Source

Thrown at litellm/cost_calculator.py:1819

) -> tuple[float, float]:
    """
    Args:
        model: str - model name
        custom_llm_provider: Optional[str] - custom LLM provider
        response: Optional[Any] - response object

    Returns:
        Tuple[float, float]: cost of OCR processing

        (Parent function requires a tuple, so we return a tuple. Cost is only in the first element.)
    """
    from litellm.llms.base_llm.ocr.transformation import OCRResponse

    #########################################################
    # validate it's an OCR response
    #########################################################
    if response is None or not isinstance(response, OCRResponse):
        raise ValueError(f"response must be of type OCRResponse got type={type(response)}")

    if response.usage_info is None:
        raise ValueError("OCR response usage_info is None")

    try:
        model_info: ModelInfo | None = litellm.get_model_info(model=model, custom_llm_provider=custom_llm_provider)
    except Exception:
        model_info = None

    credits: Final = getattr(response.usage_info, "credits", None)
    cost_per_credit = None
    if model_info is not None:
        cost_per_credit = model_info.get("ocr_cost_per_credit")
    if credits is not None and cost_per_credit is not None:
        return cost_per_credit * credits, 0.0

    ocr_cost_per_page: float | None = None
    if model_info is not None:

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. Return/transform to OCRResponse in your OCR handler (subclass the base transformation so LiteLLM yields OCRResponse).
  2. If you have raw data, construct OCRResponse(...) (with usage_info) before computing cost.
  3. Update LiteLLM so your provider's OCR transformation matches the current base interface.
  4. Skip LiteLLM cost calc for non-conforming responses and price manually.

Example fix

# before
resp = await client.post(...)  # raw dict
cost = litellm.cost_calculator.ocr_cost_calculator(response=resp.json(), model=...)

# after
from litellm.llms.base_llm.ocr.transformation import OCRResponse, OCRUsageInfo
ocr = OCRResponse(..., usage_info=OCRUsageInfo(pages_processed=3, credits=None))
cost = litellm.cost_calculator.ocr_cost_calculator(response=ocr, model=...)
Defensive patterns

Strategy: type-guard

Validate before calling

from litellm.llms.base_llm.ocr.transformation import OCRResponse
if not isinstance(response, OCRResponse):
    raise TypeError(f"expected OCRResponse, got {type(response).__name__}")

Type guard

from litellm.llms.base_llm.ocr.transformation import OCRResponse

def is_ocr_response(resp) -> bool:
    return isinstance(resp, OCRResponse)

Try / catch

try:
    cost = ocr_cost_fn(response=response, model=model)
except ValueError as e:
    if "must be of type OCRResponse" in str(e):
        raise TypeError(f"handler returned {type(response).__name__}; fix transformation") from e
    raise

Prevention

When it happens

Trigger: Calling the OCR cost path (ocr cost calculation invoked via cost_calculator for call_type OCR) with a plain dict, a provider-native response, or None instead of an OCRResponse instance.

Common situations: Custom OCR handlers returning raw JSON; version drift where the expected return type changed to OCRResponse; passing the unwrapped HTTP body instead of the transformed response.

Related errors


AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15). Data as JSON: /api/errors/de02ad7e2b6c50d8. Report an issue: GitHub.