{"record":{"id":"ef876c38f474dde8","repo":"BerriAI/litellm","slug":"expected-document-dict-got-type-document-ef876c","errorCode":null,"errorMessage":"Expected document dict, got {type(document)}","messagePattern":"Expected document dict, got (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/llms/vertex_ai/ocr/deepseek_transformation.py","lineNumber":157,"sourceCode":"\n        Converts OCR document format to the Vertex AI DeepSeek OCR payload:\n        - Input: {\"type\": \"image_url\", \"image_url\": \"gs://...\"}\n        - Output: {\"model\": \"deepseek-ai/deepseek-ocr-maas\", \"messages\": [{\"role\": \"user\", \"content\": [{\"type\": \"image_url\", \"image_url\": \"gs://...\"}]}]}\n\n        Args:\n            model: Model name (e.g., \"deepseek-ai/deepseek-ocr-maas\")\n            document: Document dict from user (Mistral OCR format)\n            optional_params: Already mapped optional parameters\n            headers: Request headers\n            **kwargs: Additional arguments\n\n        Returns:\n            OCRRequestData with JSON data for the DeepSeek OCR endpoint\n        \"\"\"\n        verbose_logger.debug(\"Vertex AI DeepSeek OCR transform_ocr_request (sync) called\")\n\n        if not isinstance(document, dict):\n            raise ValueError(f\"Expected document dict, got {type(document)}\")\n\n        # Extract document type and URL\n        doc_type: Final = document.get(\"type\")\n        image_url = None\n        document_url = None\n\n        if doc_type == \"image_url\":\n            image_url = document.get(\"image_url\", \"\")\n        elif doc_type == \"document_url\":\n            document_url = document.get(\"document_url\", \"\")\n        else:\n            raise ValueError(f\"Unsupported document type: {doc_type}. Expected 'image_url' or 'document_url'\")\n\n        # Build DeepSeek OCR message content\n        content_item = {}\n        if image_url:\n            content_item = {\"type\": \"image_url\", \"image_url\": image_url}\n        elif document_url:","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/llms/vertex_ai/ocr/deepseek_transformation.py#L139-L175","documentation":"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.","triggerScenarios":"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.","commonSituations":"Porting code from an API that takes a plain URL string; forwarding unvalidated user input; forgetting json.loads on a JSON-encoded body.","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)"],"exampleFix":"# before\nresp = litellm.ocr(model='vertex_ai/deepseek-ai/deepseek-ocr', document='https://example.com/doc.pdf')\n\n# after\nresp = litellm.ocr(\n    model='vertex_ai/deepseek-ai/deepseek-ocr',\n    document={'type': 'document_url', 'document_url': 'https://example.com/doc.pdf'},\n)","handlingStrategy":"type-guard","validationCode":"def is_ocr_document(doc: object) -> bool:\n    return (\n        isinstance(doc, dict)\n        and isinstance(doc.get('type'), str)\n        and doc['type'] in ('image_url', 'document_url')\n    )\n\nassert is_ocr_document(document), f'document must be a dict, got {type(document)}'","typeGuard":"from typing import Any\n\ndef is_ocr_document(value: Any) -> bool:\n    '''Narrow the OCR `document` argument to the accepted dict shape.'''\n    if not isinstance(value, dict):\n        return False\n    if value.get('type') not in ('image_url', 'document_url'):\n        return False\n    url_key = value['type']\n    return isinstance(value.get(url_key), str) and bool(value[url_key])","tryCatchPattern":"try:\n    resp = litellm.ocr(model=model, document=document)\nexcept ValueError as e:\n    if str(e).startswith('Expected document dict'):\n        document = {'type': 'document_url', 'document_url': str(document)}\n        resp = litellm.ocr(model=model, document=document)\n    else:\n        raise","preventionTips":["Always construct the document dict at your application boundary","Parse JSON payloads before passing them to the OCR API","Reject non-dict documents in your own input validation layer"],"tags":["vertex-ai","ocr","request-validation","type-error"],"backgroundTag":"invalid-request-payload","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}