From 4029a24f0cd2cf6cf32cb5f03150e9f54bfbdcbc Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Fri, 28 Aug 2026 03:56:41 -0700 Subject: [PATCH] fix(vision): degrade image validation gracefully when Pillow is missing Pillow is an optional dependency in this codebase (every other PIL use in tools/vision_tools.py imports lazily and falls back). Both salvaged validators now distinguish 'PIL missing' (pass through, header-only sniff) from 'decode failed' (reject), so a Pillow-less install keeps working instead of rejecting every PNG. Follow-up to salvaged #53307 (@CannibalKush) and #76896 (@HaiyiMei). --- tools/vision_tools.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tools/vision_tools.py b/tools/vision_tools.py index d1d34c4d8f..50c323ee2e 100644 --- a/tools/vision_tools.py +++ b/tools/vision_tools.py @@ -254,9 +254,15 @@ def _detect_image_mime_type_from_bytes(data: bytes) -> Optional[str]: if header.startswith(b"\x89PNG\r\n\x1a\n"): # Magic bytes alone are insufficient: native vision history is # immutable, so reject corrupt PNGs before they can be embedded. + # Pillow is an optional dependency — when it is missing we fall back + # to header-only sniffing (the full-decode gate in + # _validate_raster_image_decodable is likewise skipped without PIL); + # only an actual failed verify() rejects the bytes. try: from PIL import Image - + except ImportError: + return "image/png" + try: with Image.open(BytesIO(data)) as image: image.verify() except Exception: @@ -410,7 +416,11 @@ def _validate_raster_image_decodable(image_path: Path) -> Optional[str]: try: from PIL import Image as _PILImage from PIL import ImageSequence as _PILImageSequence - + except ImportError: + # Pillow is optional — without it we cannot decode-validate, so pass + # the image through unvalidated rather than rejecting everything. + return None + try: with _PILImage.open(image_path) as image: image.verify() with _PILImage.open(image_path) as image: