BerriAI/litellm · error · ValueError

Expected document dict, got {type(document)}

Error message

Expected document dict, got {type(document)}

What it means

ValueError raised in the DeepSeek OCR request transformer when the `document` argument is not a Python dict. The Mistral-compatible OCR interface expects document as {'type': ..., 'image_url'|'document_url': ...}; the isinstance(document, dict) check fails for strings, lists, or None before any request is built.

Source

Thrown at litellm/llms/vertex_ai/ocr/deepseek_transformation.py:157

        Converts OCR document format to the Vertex AI DeepSeek OCR payload:
        - Input: {"type": "image_url", "image_url": "gs://..."}
        - Output: {"model": "deepseek-ai/deepseek-ocr-maas", "messages": [{"role": "user", "content": [{"type": "image_url", "image_url": "gs://..."}]}]}

        Args:
            model: Model name (e.g., "deepseek-ai/deepseek-ocr-maas")
            document: Document dict from user (Mistral OCR format)
            optional_params: Already mapped optional parameters
            headers: Request headers
            **kwargs: Additional arguments

        Returns:
            OCRRequestData with JSON data for the DeepSeek OCR endpoint
        """
        verbose_logger.debug("Vertex AI DeepSeek OCR transform_ocr_request (sync) called")

        if not isinstance(document, dict):
            raise ValueError(f"Expected document dict, got {type(document)}")

        # Extract document type and URL
        doc_type: Final = document.get("type")
        image_url = None
        document_url = None

        if doc_type == "image_url":
            image_url = document.get("image_url", "")
        elif doc_type == "document_url":
            document_url = document.get("document_url", "")
        else:
            raise ValueError(f"Unsupported document type: {doc_type}. Expected 'image_url' or 'document_url'")

        # Build DeepSeek OCR message content
        content_item = {}
        if image_url:
            content_item = {"type": "image_url", "image_url": image_url}
        elif document_url:

View on GitHub (pinned to 77b7c6c40c)

Solutions

  1. Pass document as a dict: {'type': 'document_url', 'document_url': 'https://...'}
  2. json.loads() the payload first if it arrives as a JSON string
  3. Add a type guard before the call (see defense)

Example fix

# before
resp = litellm.ocr(model='vertex_ai/deepseek-ai/deepseek-ocr', document='https://example.com/doc.pdf')

# after
resp = litellm.ocr(
    model='vertex_ai/deepseek-ai/deepseek-ocr',
    document={'type': 'document_url', 'document_url': 'https://example.com/doc.pdf'},
)
Defensive patterns

Strategy: type-guard

Validate before calling

def is_ocr_document(doc: object) -> bool:
    return (
        isinstance(doc, dict)
        and isinstance(doc.get('type'), str)
        and doc['type'] in ('image_url', 'document_url')
    )

assert is_ocr_document(document), f'document must be a dict, got {type(document)}'

Type guard

from typing import Any

def is_ocr_document(value: Any) -> bool:
    '''Narrow the OCR `document` argument to the accepted dict shape.'''
    if not isinstance(value, dict):
        return False
    if value.get('type') not in ('image_url', 'document_url'):
        return False
    url_key = value['type']
    return isinstance(value.get(url_key), str) and bool(value[url_key])

Try / catch

try:
    resp = litellm.ocr(model=model, document=document)
except ValueError as e:
    if str(e).startswith('Expected document dict'):
        document = {'type': 'document_url', 'document_url': str(document)}
        resp = litellm.ocr(model=model, document=document)
    else:
        raise

Prevention

When it happens

Trigger: Calling OCR with document='https://host/doc.pdf' (bare string), document=[{...}] (list), or an unparsed JSON string; passing None because the caller's payload construction failed silently.

Common situations: Porting code from an API that takes a plain URL string; forwarding unvalidated user input; forgetting json.loads on a JSON-encoded body.

Related errors


AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18). Data as JSON: /api/errors/ef876c38f474dde8. Report an issue: GitHub.