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 sync Vertex AI OCR request transformer (transform_ocr_request) when `document` is not a Python dict. The handler expects {'type': ..., 'image_url'|'document_url': ...}; the isinstance check fails for strings, lists, or None before URL-to-base64 conversion and request building start.
Source
Thrown at litellm/llms/vertex_ai/ocr/transformation.py:203
Transform OCR request for Vertex AI, converting URLs to base64 data URIs (sync).
Vertex AI OCR doesn't have internet access, so we automatically fetch
any URLs and convert them to base64 data URIs synchronously.
Args:
model: Model name
document: Document dict from user
optional_params: Already mapped optional parameters
headers: Request headers
**kwargs: Additional arguments
Returns:
OCRRequestData with JSON data
"""
verbose_logger.debug("Vertex AI OCR transform_ocr_request (sync) called")
if not isinstance(document, dict):
raise ValueError(f"Expected document dict, got {type(document)}")
# Check if we need to convert URL to base64
doc_type: Final = document.get("type")
transformed_document: Final = document.copy()
if doc_type == "document_url":
document_url: Final = document.get("document_url", "")
# If it's not already a data URI, convert it
if document_url and not document_url.startswith("data:"):
verbose_logger.debug("Vertex AI OCR: Converting document URL to base64 data URI (sync)")
data_uri = self._convert_url_to_data_uri_sync(url=document_url)
transformed_document["document_url"] = data_uri
elif doc_type == "image_url":
image_url: Final = document.get("image_url", "")
# If it's not already a data URI, convert it
if image_url and not image_url.startswith("data:"):
verbose_logger.debug("Vertex AI OCR: Converting image URL to base64 data URI (sync)")
data_uri = self._convert_url_to_data_uri_sync(url=image_url)View on GitHub (pinned to 77b7c6c40c)
Solutions
- Pass document as a dict: {'type': 'document_url', 'document_url': 'https://...'}
- json.loads() the payload first if it arrives as a JSON string
- Add a type guard before the call (see defense)
Example fix
# before
resp = litellm.ocr(model='vertex_ai/ocr-model', document='https://example.com/doc.pdf')
# after
resp = litellm.ocr(
model='vertex_ai/ocr-model',
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 doc.get('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:
'''True when value is a dict of the shape the Vertex OCR handler accepts.'''
if not isinstance(value, dict):
return False
t = value.get('type')
if t not in ('image_url', 'document_url'):
return False
return isinstance(value.get(t), str) and bool(value[t]) 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
- Build the document dict at the boundary of your app
- json.loads() any serialized payload before the call
- Reject non-dict documents in your input validation layer
When it happens
Trigger: Calling sync OCR with document='https://...' (bare string) or document=[{...}] (list); forwarding an unparsed JSON string as the document.
Common situations: Reusing code from APIs that accept bare URLs; missing json.loads on serialized payloads; untyped request builders passing through whatever the caller supplied.
Related errors
- Expected document dict, got {type(document)}
- Unsupported document type: {doc_type}. Expected 'image_url'
- Missing vertex_project - Set VERTEXAI_PROJECT environment va
- No choices in DeepSeek OCR response
- No content in DeepSeek OCR response
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/ac38c5a090c56652.
Report an issue: GitHub.