{"record":{"id":"ac38c5a090c56652","repo":"BerriAI/litellm","slug":"expected-document-dict-got-type-document-ac38c5","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/transformation.py","lineNumber":203,"sourceCode":"        Transform OCR request for Vertex AI, converting URLs to base64 data URIs (sync).\n\n        Vertex AI OCR doesn't have internet access, so we automatically fetch\n        any URLs and convert them to base64 data URIs synchronously.\n\n        Args:\n            model: Model name\n            document: Document dict from user\n            optional_params: Already mapped optional parameters\n            headers: Request headers\n            **kwargs: Additional arguments\n\n        Returns:\n            OCRRequestData with JSON data\n        \"\"\"\n        verbose_logger.debug(\"Vertex AI 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        # Check if we need to convert URL to base64\n        doc_type: Final = document.get(\"type\")\n        transformed_document: Final = document.copy()\n\n        if doc_type == \"document_url\":\n            document_url: Final = document.get(\"document_url\", \"\")\n            # If it's not already a data URI, convert it\n            if document_url and not document_url.startswith(\"data:\"):\n                verbose_logger.debug(\"Vertex AI OCR: Converting document URL to base64 data URI (sync)\")\n                data_uri = self._convert_url_to_data_uri_sync(url=document_url)\n                transformed_document[\"document_url\"] = data_uri\n        elif doc_type == \"image_url\":\n            image_url: Final = document.get(\"image_url\", \"\")\n            # If it's not already a data URI, convert it\n            if image_url and not image_url.startswith(\"data:\"):\n                verbose_logger.debug(\"Vertex AI OCR: Converting image URL to base64 data URI (sync)\")\n                data_uri = self._convert_url_to_data_uri_sync(url=image_url)","sourceCodeStart":185,"sourceCodeEnd":221,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/llms/vertex_ai/ocr/transformation.py#L185-L221","documentation":"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.","triggerScenarios":"Calling sync OCR with document='https://...' (bare string) or document=[{...}] (list); forwarding an unparsed JSON string as the document.","commonSituations":"Reusing code from APIs that accept bare URLs; missing json.loads on serialized payloads; untyped request builders passing through whatever the caller supplied.","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/ocr-model', document='https://example.com/doc.pdf')\n\n# after\nresp = litellm.ocr(\n    model='vertex_ai/ocr-model',\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 doc.get('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    '''True when value is a dict of the shape the Vertex OCR handler accepts.'''\n    if not isinstance(value, dict):\n        return False\n    t = value.get('type')\n    if t not in ('image_url', 'document_url'):\n        return False\n    return isinstance(value.get(t), str) and bool(value[t])","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":["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"],"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-14T00:17:10.932Z"}