{"record":{"id":"21f032d4dae0344d","repo":"BerriAI/litellm","slug":"vertex-ai-gemini-image-edit-requires-at-least-one","errorCode":null,"errorMessage":"Vertex AI Gemini image edit requires at least one image.","messagePattern":"Vertex AI Gemini image edit requires at least one image\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/llms/vertex_ai/image_edit/vertex_gemini_transformation.py","lineNumber":157,"sourceCode":"        if not vertex_project or not vertex_location:\n            raise ValueError(\"vertex_project and vertex_location are required for Vertex AI\")\n\n        base_url: Final = get_vertex_base_url(vertex_location)\n\n        return f\"{base_url}/v1/projects/{vertex_project}/locations/{vertex_location}/publishers/google/models/{model_name}:generateContent\"\n\n    def transform_image_edit_request(\n        self,\n        model: str,\n        prompt: str | None,\n        image: FileTypes | None,\n        image_edit_optional_request_params: dict[str, Any],\n        litellm_params: GenericLiteLLMParams,\n        headers: dict,\n    ) -> tuple[dict[str, Any], RequestFiles | None]:\n        inline_parts: Final = self._prepare_inline_image_parts(image) if image else []\n        if not inline_parts:\n            raise ValueError(\"Vertex AI Gemini image edit requires at least one image.\")\n\n        # Build parts list with image and prompt (if provided)\n        parts: Final = inline_parts.copy()\n        if prompt is not None and prompt != \"\":\n            parts.append({\"text\": prompt})\n\n        # Correct format for Vertex AI Gemini image editing\n        contents: Final = {\"role\": \"USER\", \"parts\": parts}\n\n        request_body: Final[dict[str, Any]] = {\"contents\": contents}\n\n        # Generation config with proper structure for image editing\n        generation_config: Final[dict[str, Any]] = {\"response_modalities\": [\"IMAGE\"]}\n\n        # Add image-specific configuration\n        image_config: Final[dict[str, Any]] = {}\n        if \"aspectRatio\" in image_edit_optional_request_params:\n            image_config[\"aspect_ratio\"] = image_edit_optional_request_params[\"aspectRatio\"]","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/llms/vertex_ai/image_edit/vertex_gemini_transformation.py#L139-L175","documentation":"The Vertex AI Gemini image-edit handler encodes every input image as an inlineData part inside the generateContent request. It refuses to build a request containing zero image parts: if image is None, falsy (e.g. b''), or a list whose entries are all None, inline_parts comes back empty and this ValueError is raised. Prompt-only edits are not supported through this path — that is image generation, not edit.","triggerScenarios":"litellm.image_edit(model='vertex_ai/gemini-...', prompt='make it snowy') with the image kwarg omitted or image=None; passing image=[None, None]; passing image=b'' (empty bytes are falsy so the prep step is skipped).","commonSituations":"Porting OpenAI images/edits workflows where the image was optional; upload pipelines that pass a list comprehension result which filtered everything out; UI code where the user submitted a prompt without attaching a photo.","solutions":["Always pass at least one image as bytes, io.BytesIO, or a binary file object (open with 'rb')","Filter None entries out of image lists before calling: image=[i for i in imgs if i is not None]","If you have no input image, use litellm.image_generation(model='vertex_ai/imagen-...') instead of image_edit"],"exampleFix":"# before\nresp = litellm.image_edit(\n    model='vertex_ai/gemini-2.5-flash-image',\n    prompt='add a hat',\n    image=None,\n)\n\n# after\nwith open('cat.png', 'rb') as f:\n    resp = litellm.image_edit(\n        model='vertex_ai/gemini-2.5-flash-image',\n        prompt='add a hat',\n        image=f,\n    )","handlingStrategy":"validation","validationCode":"def usable_images(image) -> bool:\n    if image is None or image == b'':\n        return False\n    if isinstance(image, (list, tuple)):\n        return any(img is not None and img != b'' for img in image)\n    return True\n\nassert usable_images(image), 'Gemini image edit requires at least one image'","typeGuard":null,"tryCatchPattern":"try:\n    resp = litellm.image_edit(model='vertex_ai/gemini-2.5-flash-image', prompt=p, image=image)\nexcept ValueError as e:\n    if 'requires at least one image' in str(e):\n        return bad_request('attach an image to edit')\n    raise","preventionTips":["Make the image field mandatory in your API schema for edit endpoints","Filter None entries from image lists before calling litellm","Route prompt-only requests to image_generation, not image_edit"],"tags":["vertex-ai","gemini","image-edit","input-validation","missing-parameter"],"backgroundTag":"missing-required-parameter","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}