hiyouga/LlamaFactory · error · ValueError

Invalid image found in video frames.

Error message

Invalid image found in video frames.

What it means

Thrown in BasePlugin._regularize_videos when a video supplied as a nested list of frames contains a frame that is not a valid image, not a dict descriptor, and not an existing filesystem path. LlamaFactory accepts pre-sampled frame lists, but each frame must be loadable (PIL Image, bytes dict, or path).

Source

Thrown at src/llamafactory/data/mm_plugin.py:287

                    image = Image.open(image["path"])

            if not isinstance(image, ImageObject):
                raise ValueError(f"Expect input is a list of images, but got {type(image)}.")

            results.append(self._preprocess_image(image, **kwargs))

        return {"images": results}

    def _regularize_videos(self, videos: list["VideoInput"], **kwargs) -> "RegularizedVideoOutput":
        r"""Regularizes videos to avoid error. Including reading, resizing and converting."""
        results = []
        durations = []
        for video in videos:
            frames: list[ImageObject] = []
            if _check_video_is_nested_images(video):
                for frame in video:
                    if not is_valid_image(frame) and not isinstance(frame, dict) and not os.path.exists(frame):
                        raise ValueError("Invalid image found in video frames.")
                frames = video
                durations.append(len(frames) / kwargs.get("video_fps", 2.0))
            else:
                container = av.open(video, "r")
                video_stream = next(stream for stream in container.streams if stream.type == "video")
                sample_indices = self._get_video_sample_indices(video_stream, **kwargs)
                container.seek(0)
                for frame_idx, frame in enumerate(container.decode(video_stream)):
                    if frame_idx in sample_indices:
                        frames.append(frame.to_image())

                if video_stream.duration is None:
                    durations.append(len(frames) / kwargs.get("video_fps", 2.0))
                else:
                    durations.append(float(video_stream.duration * video_stream.time_base))

            frames = self._regularize_images(frames, **kwargs)["images"]
            results.append(frames)

View on GitHub (pinned to f28afaf635)

Solutions

  1. Verify every frame path exists from the process's actual cwd; use absolute paths or set dataset_dir correctly.
  2. Convert numpy/array frames to PIL Images or {'bytes': ...} dicts.
  3. Drop or repair rows with None frames.

Example fix

# before
"videos": [["frames/f0001.jpg", "frames/f0002.jpg"]]  # relative, cwd-dependent
# after
"videos": [["/data/vid1/f0001.jpg", "/data/vid1/f0002.jpg"]]
Defensive patterns

Strategy: validation

Validate before calling

import os
from PIL import Image

def frames_ok(frames):
    for f in frames:
        if isinstance(f, Image.Image) or isinstance(f, dict):
            continue
        if isinstance(f, str) and os.path.isfile(f):
            continue
        return False
    return True

Prevention

When it happens

Trigger: A dataset 'videos' column containing [[frame1, frame2], ...] where a frame is None, a numpy array, or a path string to a file that does not exist (os.path.exists fails, e.g. relative path resolved from the wrong cwd).

Common situations: Pre-sampled frame datasets converted from video files where the frame paths are relative and training runs from a different working directory; frames serialized as arrays; missing frame files after moving the dataset.

Related errors


AI-assisted analysis of hiyouga/LlamaFactory@f28afaf635 (2026-08-14). Data as JSON: /api/errors/c526f5639634b43d. Report an issue: GitHub.