{"record":{"id":"2fc2a842812b7e58","repo":"hiyouga/LlamaFactory","slug":"expect-input-is-a-list-of-images-but-got-type-im","errorCode":null,"errorMessage":"Expect input is a list of images, but got {type(image)}.","messagePattern":"Expect input is a list of images, but got (.+?)\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/llamafactory/data/mm_plugin.py","lineNumber":272,"sourceCode":"        r\"\"\"Build metadata used to expand video tokens without decoding frames.\"\"\"\n        return None\n\n    def _regularize_images(self, images: list[\"ImageInput\"], **kwargs) -> \"RegularizedImageOutput\":\n        r\"\"\"Regularize images to avoid error. Including reading and pre-processing.\"\"\"\n        results = []\n        for image in images:\n            if isinstance(image, (str, BinaryIO)):\n                image = Image.open(image)\n            elif isinstance(image, bytes):\n                image = Image.open(BytesIO(image))\n            elif isinstance(image, dict):\n                if image[\"bytes\"] is not None:\n                    image = Image.open(BytesIO(image[\"bytes\"]))\n                else:\n                    image = Image.open(image[\"path\"])\n\n            if not isinstance(image, ImageObject):\n                raise ValueError(f\"Expect input is a list of images, but got {type(image)}.\")\n\n            results.append(self._preprocess_image(image, **kwargs))\n\n        return {\"images\": results}\n\n    def _regularize_videos(self, videos: list[\"VideoInput\"], **kwargs) -> \"RegularizedVideoOutput\":\n        r\"\"\"Regularizes videos to avoid error. Including reading, resizing and converting.\"\"\"\n        results = []\n        durations = []\n        for video in videos:\n            frames: list[ImageObject] = []\n            if _check_video_is_nested_images(video):\n                for frame in video:\n                    if not is_valid_image(frame) and not isinstance(frame, dict) and not os.path.exists(frame):\n                        raise ValueError(\"Invalid image found in video frames.\")\n                frames = video\n                durations.append(len(frames) / kwargs.get(\"video_fps\", 2.0))\n            else:","sourceCodeStart":254,"sourceCodeEnd":290,"githubUrl":"https://github.com/hiyouga/LlamaFactory/blob/f28afaf6355af515454dfb16c97d728307c93897/src/llamafactory/data/mm_plugin.py#L254-L290","documentation":"Thrown in BasePlugin._regularize_images when an element of the `images` list cannot be coerced into a PIL Image. Supported inputs are: file path str / file-like object (opened via Image.open), raw bytes, a dict with 'bytes' or 'path' keys, or an already-loaded PIL Image. Anything else (int, None, numpy array, broken object) reaches the isinstance check and fails.","triggerScenarios":"Passing images as numpy arrays, torch tensors, None entries, or a single Image instead of a list to _regularize_images / process_messages. Also a dict without 'bytes'/'path' keys, or an object whose type is not PIL.Image.Image.","commonSituations":"Datasets stored as decoded numpy frames; a column that is null for some rows; iterating a column that yields scalars; HF datasets pushing an unexpected Arrow type.","solutions":["Convert each image to PIL before passing: Image.fromarray(arr) for numpy, or pass file paths / {'bytes': ..., 'path': ...} dicts.","Filter or repair rows with null/missing image values in the dataset.","Ensure you pass a list of images, not a bare image or nested lists."],"exampleFix":"# before\nimages = [np_array]  # numpy RGB array\n# after\nfrom PIL import Image\nimages = [Image.fromarray(np_array)]","handlingStrategy":"type-guard","validationCode":"from PIL import Image\n\ndef valid_image_inputs(images):\n    for im in images:\n        if isinstance(im, (str, bytes, dict)) or isinstance(im, Image.Image):\n            continue\n        if hasattr(im, 'read'):  # file-like\n            continue\n        return False\n    return True","typeGuard":"from PIL import Image\nfrom typing import Union\n\nImageInputOK = Union[str, bytes, dict, Image.Image]\n\ndef is_image_input(x) -> bool:\n    return isinstance(x, (str, bytes, dict, Image.Image)) or hasattr(x, 'read')","tryCatchPattern":"try:\n    mm = plugin.process_messages(messages, images, [], [], processor)\nexcept ValueError as e:\n    if 'list of images' in str(e):\n        images = [Image.open(p) if isinstance(p, str) else p for p in images]\n    else:\n        raise","preventionTips":["Store images in datasets as paths or {'bytes':..., 'path':...} dicts, never raw arrays.","Drop rows with null image fields during data prep."],"tags":["multimodal","image","type-validation","data-preprocessing"],"backgroundTag":null,"analyzedSha":"f28afaf6355af515454dfb16c97d728307c93897","analyzedAt":"2026-08-14T21:57:28.298Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}