BerriAI/litellm · error · ValueError

Unsupported document type: {doc_type}. Expected 'image_url'

Error message

Unsupported document type: {doc_type}. Expected 'image_url' or 'document_url'

What it means

ValueError raised when the document dict's 'type' field is neither 'image_url' nor 'document_url'. The DeepSeek OCR transformer branches only on those two literals, so a missing 'type' key (doc_type is None) or any other spelling is rejected before a request is built.

Source

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

        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:
            # For document URLs, we use image_url type as well (Vertex AI supports both)
            content_item = {"type": "image_url", "image_url": document_url}

        # Build DeepSeek OCR request
        data: Final = {
            "model": "deepseek-ai/" + model,
            "messages": [{"role": "user", "content": [content_item]}],
        }

        # Add optional parameters (stream, temperature, etc.)
        deepseek_ocr_params: Final = {}
        for key, value in optional_params.items():

View on GitHub (pinned to 77b7c6c40c)

Solutions

  1. Use exactly 'image_url' or 'document_url' as the type value
  2. Pair it with the matching sibling key: 'image_url' for images, 'document_url' for PDFs/documents
  3. Validate the enum before the call (see defense)

Example fix

# before
litellm.ocr(model='vertex_ai/deepseek-ai/deepseek-ocr', document={'type': 'image', 'url': img_url})

# after
litellm.ocr(model='vertex_ai/deepseek-ai/deepseek-ocr', document={'type': 'image_url', 'image_url': img_url})
Defensive patterns

Strategy: validation

Validate before calling

ALLOWED = ('image_url', 'document_url')

def normalize_document(doc: dict) -> dict:
    t = doc.get('type')
    if t == 'image':
        doc = {**doc, 'type': 'image_url'}
    elif t == 'file':
        doc = {**doc, 'type': 'document_url'}
    assert doc.get('type') in ALLOWED, f'type must be one of {ALLOWED}, got {t!r}'
    return doc

Type guard

def has_supported_doc_type(doc: dict) -> bool:
    return doc.get('type') in ('image_url', 'document_url')

Prevention

When it happens

Trigger: document={'type': 'image', ...} or {'type': 'file', ...}; the 'type' key omitted entirely so doc_type is None; values copied from another OCR vendor's schema.

Common situations: Schema drift between OCR providers (Mistral uses image_url/document_url while other APIs use 'image' or 'file'); unvalidated user-supplied document descriptors.

Related errors


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