{"record":{"id":"c11ecd63a95987fc","repo":"BerriAI/litellm","slug":"unsupported-image-value-image-provide-a-gcs-u","errorCode":null,"errorMessage":"Unsupported image value '{image}'. Provide a GCS URI (gs://...), a dict with 'gcsUri' or 'bytesBase64Encoded'/'mimeType', or a binary file-like object.","messagePattern":"Unsupported image value '(.+?)'\\. Provide a GCS URI \\(gs://\\.\\.\\.\\), a dict with 'gcsUri' or 'bytesBase64Encoded'/'mimeType', or a binary file-like object\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/llms/vertex_ai/videos/transformation.py","lineNumber":338,"sourceCode":"        instance_dict: Final[dict[str, object]] = {\"prompt\": prompt}\n        params_copy: Final = video_create_optional_request_params.copy()\n\n        # Check if user wants to provide full instance dict\n        if \"instances\" in params_copy and isinstance(params_copy[\"instances\"], dict):\n            # Replace/merge with user-provided instance\n            instance_dict.update(params_copy[\"instances\"])\n            params_copy.pop(\"instances\")\n        elif \"image\" in params_copy and params_copy[\"image\"] is not None:\n            image: Final = params_copy[\"image\"]\n            if isinstance(image, dict):\n                # Already in Vertex format e.g. {\"gcsUri\": \"gs://...\"} or\n                # {\"bytesBase64Encoded\": \"...\", \"mimeType\": \"...\"}\n                image_data = image\n            elif isinstance(image, str) and image.startswith(\"gs://\"):\n                # Bare GCS URI — Vertex AI accepts gcsUri natively, no download needed\n                image_data = {\"gcsUri\": image}\n            elif isinstance(image, str):\n                raise ValueError(\n                    f\"Unsupported image value '{image}'. \"\n                    \"Provide a GCS URI (gs://...), a dict with 'gcsUri' or \"\n                    \"'bytesBase64Encoded'/'mimeType', or a binary file-like object.\"\n                )\n            else:\n                # File-like object — encode to base64\n                image_data = _convert_image_to_vertex_format(image)\n            instance_dict[\"image\"] = image_data\n            params_copy.pop(\"image\")\n\n        # Extract a nested \"parameters\" block that map_openai_params may have placed\n        # inside params_copy (e.g. from provider-specific pass-through).  Merging it\n        # flat prevents the double-nesting bug:\n        #   {\"parameters\": {\"parameters\": {...}}}  ← wrong\n        #   {\"parameters\": {...}}                  ← correct\n        nested_params: Final = params_copy.pop(\"parameters\", None)\n        vertex_params: Final[dict[str, object]] = {}\n        if isinstance(nested_params, dict):","sourceCodeStart":320,"sourceCodeEnd":356,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/llms/vertex_ai/videos/transformation.py#L320-L356","documentation":"Input-validation error in the Veo request transformer for the `image` parameter (used for image-to-video). The transformer accepts exactly three shapes: a dict already in Vertex format ({\"gcsUri\": ...} or {\"bytesBase64Encoded\": ..., \"mimeType\": ...}), a string starting with gs://, or a non-str/file-like object that gets base64-encoded. Any other string — an http(s) URL, a local file path, or arbitrary text — hits this raise before any API call is made.","triggerScenarios":"Passing image=\"https://storage.googleapis.com/bucket/img.png\" (http URL, not gs://), image=\"/tmp/frame.png\" (local path), or a base64 string without wrapping it in {\"bytesBase64Encoded\": ...} to litellm.video_generation with a Veo model.","commonSituations":"Porting code from Gemini API where arbitrary image URLs were accepted; feeding model outputs or scraped URLs directly; assuming base64 strings are auto-detected.","solutions":["Use a GCS URI in the same project/region as the Veo call: image=\"gs://my-bucket/frame.png\".","Wrap base64 or remote bytes explicitly: image={\"bytesBase64Encoded\": b64, \"mimeType\": \"image/png\"}.","For local files, open them and pass the file object: image=open(\"frame.png\", \"rb\") so the transformer base64-encodes it.","For an http(s) URL, download the bytes yourself first, then pass the dict form."],"exampleFix":"# before\nlitellm.video_generation(\n    model=\"vertex_ai/veo-2.0-generate-001\",\n    prompt=\"animate this\",\n    image=\"https://example.com/frame.png\",  # raises\n)\n\n# after\nimport base64, requests\nb64 = base64.b64encode(requests.get(\"https://example.com/frame.png\").content).decode()\nlitellm.video_generation(\n    model=\"vertex_ai/veo-2.0-generate-001\",\n    prompt=\"animate this\",\n    image={\"bytesBase64Encoded\": b64, \"mimeType\": \"image/png\"},\n)","handlingStrategy":"type-guard","validationCode":"def normalize_veo_image(image):\n    if isinstance(image, dict) and (\"gcsUri\" in image or \"bytesBase64Encoded\" in image):\n        return image\n    if isinstance(image, str) and image.startswith(\"gs://\"):\n        return {\"gcsUri\": image}\n    if hasattr(image, \"read\"):  # file-like\n        return {\"bytesBase64Encoded\": base64.b64encode(image.read()).decode(), \"mimeType\": \"image/png\"}\n    raise ValueError(\"image must be gs:// URI, Vertex-format dict, or binary file object\")","typeGuard":"def is_valid_veo_image(image) -> bool:\n    if isinstance(image, dict):\n        return \"gcsUri\" in image or \"bytesBase64Encoded\" in image\n    if isinstance(image, str):\n        return image.startswith(\"gs://\")\n    return hasattr(image, \"read\")","tryCatchPattern":"try:\n    v = litellm.video_generation(model=\"vertex_ai/veo-2.0-generate-001\", prompt=p, image=img)\nexcept ValueError as e:\n    if \"Unsupported image value\" in str(e):\n        img = normalize_veo_image(img)\n        v = litellm.video_generation(model=\"vertex_ai/veo-2.0-generate-001\", prompt=p, image=img)\n    else:\n        raise","preventionTips":["Convert http(s) URLs to bytesBase64Encoded dicts before passing them to Veo.","Keep images in GCS buckets in the same project/region for the cheapest path (gcsUri).","Wrap image inputs in a normalize_veo_image() helper at your app boundary."],"tags":["vertex-ai","veo","image-input","validation","video-generation"],"backgroundTag":"invalid-request-payload","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}