BerriAI/litellm · error · ValueError

Expected document dict, got {type(document)}

Error message

Expected document dict, got {type(document)}

What it means

Raised by litellm's Mistral OCR transformer in transform_ocr_request when the `document` argument is not a Python dict. Mistral's native OCR API expects a document object (e.g. {"type": "document_url", "document_url": ...} or image_url variant), and litellm passes the user-supplied document through verbatim, so it must already be a dict in Mistral format.

Source

Thrown at litellm/llms/mistral/ocr/transformation.py:183

            "include_image_base64": false,  # optional
            ...
        }

        Args:
            model: Model name (e.g., "mistral-ocr-latest")
            document: Document dict from user (Mistral format) - already validated in main.py
            optional_params: Already mapped optional parameters
            headers: Request headers

        Returns:
            OCRRequestData with JSON data
        """
        verbose_logger.debug("Mistral OCR transform_ocr_request - model: %s", model)

        # Document parameter is the Mistral-format dict from the user
        # Just pass it through as-is to the Mistral API
        if not isinstance(document, dict):
            raise ValueError(f"Expected document dict, got {type(document)}")

        # Build request data - use document dict directly
        data: Final = {
            "model": model,
            "document": document,  # Pass through the Mistral-format document dict
        }

        # Add all optional parameters from the already-mapped optional_params
        data.update(optional_params)

        # No multipart files - using JSON
        return OCRRequestData(data=data, files=None)

    def transform_ocr_response(
        self,
        model: str,
        raw_response: httpx.Response,
        logging_obj: Any,

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. Pass document as a Mistral-format dict: {"type": "document_url", "document_url": "https://..."} for PDFs or {"type": "image_url", "image_url": "https://..."} for images.
  2. If you built a Pydantic/dataclass object, call .model_dump() / .dict() first.
  3. Never pass a raw URL string or file path as document for mistral models.

Example fix

# before
litellm.ocr(model="mistral/mistral-ocr-latest", document="https://x.com/a.pdf")

# after
litellm.ocr(
    model="mistral/mistral-ocr-latest",
    document={"type": "document_url", "document_url": "https://x.com/a.pdf"},
)
Defensive patterns

Strategy: type-guard

Validate before calling

def is_mistral_document(doc: object) -> bool:
    return isinstance(doc, dict) and doc.get("type") in {"document_url", "image_url"} and (
        "document_url" in doc or "image_url" in doc
    )

Type guard

from typing import Any, TypeGuard

def is_mistral_ocr_document(value: Any) -> TypeGuard[dict]:
    return (
        isinstance(value, dict)
        and isinstance(value.get("type"), str)
        and any(k in value for k in ("document_url", "image_url"))
    )

Try / catch

try:
    litellm.ocr(model="mistral/mistral-ocr-latest", document=doc)
except ValueError as e:
    if "Expected document dict" in str(e):
        raise TypeError("document must be a Mistral-format dict") from e
    raise

Prevention

When it happens

Trigger: Calling litellm.ocr(model='mistral/mistral-ocr-latest', document=<string or object>) — e.g. passing a bare URL string, a Pydantic model, or a file path instead of the Mistral-format dict.

Common situations: Developers coming from other OCR APIs that accept a URL string directly, or passing an unserialized object; also passing document as bytes.

Related errors


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