{"record":{"id":"55053fcfc0b22fed","repo":"crewAIInc/crewAI","slug":"unsupported-image-format-supported-formats-vali","errorCode":null,"errorMessage":"Unsupported image format. Supported formats: {valid_extensions}","messagePattern":"Unsupported image format\\. Supported formats: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"lib/crewai-tools/src/crewai_tools/tools/vision_tool/vision_tool.py","lineNumber":30,"sourceCode":"\nclass ImagePromptSchema(BaseModel):\n    \"\"\"Input for Vision Tool.\"\"\"\n\n    image_path_url: str = \"The image path or URL.\"\n\n    @field_validator(\"image_path_url\")\n    @classmethod\n    def validate_image_path_url(cls, v: str) -> str:\n        if v.startswith(\"http\"):\n            return v\n\n        path = Path(v)\n        if not path.exists():\n            raise ValueError(f\"Image file does not exist: {v}\")\n\n        valid_extensions = {\".jpg\", \".jpeg\", \".png\", \".gif\", \".webp\"}\n        if path.suffix.lower() not in valid_extensions:\n            raise ValueError(\n                f\"Unsupported image format. Supported formats: {valid_extensions}\"\n            )\n\n        return v\n\n\nclass VisionTool(BaseTool):\n    \"\"\"Tool for analyzing images using vision models.\n\n    Args:\n        llm: Optional LLM instance to use\n        model: Model identifier to use if no LLM is provided\n    \"\"\"\n\n    name: str = \"Vision Tool\"\n    description: str = (\n        \"This tool uses OpenAI's Vision API to describe the contents of an image.\"\n    )","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/crewAIInc/crewAI/blob/754d7323beb2fd042e33444a115ea2d5a47193f0/lib/crewai-tools/src/crewai_tools/tools/vision_tool/vision_tool.py#L12-L48","documentation":"VisionTool's image_path_url validator enforces a whitelist of image extensions {.jpg, .jpeg, .png, .gif, .webp} for local files. Any other suffix (checked case-insensitively) raises ValueError listing the supported formats, because the underlying vision model only accepts those MIME types.","triggerScenarios":"Calling VisionTool.run(image_path_url='/tmp/scan.tiff'), '.bmp', '.heic', '.avif', or a file with no extension; also paths with trailing dots or query-like suffixes that survive Path.suffix parsing.","commonSituations":"Users feeding TIFF/BMP/HEIC exports from cameras, scanners, or screenshots; macOS HEIC photos; downloaded files whose extension was stripped.","solutions":["Convert the image to a supported format first (e.g., with PIL: img.convert('RGB').save(p, 'PNG')).","Rename/re-save files with a supported extension after a real conversion - do not just rename, the bytes must match.","For HEIC/TIFF sources, add pillow-heif / ImageMagick conversion in your upload pipeline."],"exampleFix":"# before\nVisionTool().run(image_path_url='/tmp/scan.tiff')  # ValueError: unsupported format\n\n# after\nfrom PIL import Image\nImage.open('/tmp/scan.tiff').convert('RGB').save('/tmp/scan.png')\nVisionTool().run(image_path_url='/tmp/scan.png')","handlingStrategy":"type-guard","validationCode":"from pathlib import Path\n\nSUPPORTED = {'.jpg', '.jpeg', '.png', '.gif', '.webp'}\n\ndef ensure_supported(path: str) -> str:\n    p = Path(path)\n    if p.suffix.lower() not in SUPPORTED:\n        from PIL import Image\n        new = p.with_suffix('.png')\n        Image.open(p).convert('RGB').save(new)\n        return str(new)\n    return path","typeGuard":"from pathlib import Path\n\ndef is_supported_image(v: str) -> bool:\n    return v.startswith('http') or Path(v).suffix.lower() in {'.jpg', '.jpeg', '.png', '.gif', '.webp'}","tryCatchPattern":"try:\n    VisionTool().run(image_path_url=path)\nexcept ValueError as e:\n    if 'Unsupported image format' in str(e):\n        path = convert_with_pil(path, target='.png')  # real conversion, not rename\n        VisionTool().run(image_path_url=path)\n    else:\n        raise","preventionTips":["Convert HEIC/TIFF/BMP uploads to PNG/JPEG in your ingestion pipeline.","Filter candidate images by extension before handing paths to the model.","Never fix by renaming only - bytes must actually be re-encoded."],"tags":["validation","image-format","vision","pydantic"],"backgroundTag":null,"analyzedSha":"754d7323beb2fd042e33444a115ea2d5a47193f0","analyzedAt":"2026-08-15T04:06:56.746Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}