{"record":{"id":"b968297ca3c4b06f","repo":"vllm-project/vllm","slug":"could-not-read-enough-frames-from-video-file-path","errorCode":null,"errorMessage":"Could not read enough frames from video file {path} (expected {num_frames} frames, got {len(frames)})","messagePattern":"Could not read enough frames from video file (.+?) \\(expected (.+?) frames, got (.+?)\\)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"vllm/assets/video.py","lineNumber":68,"sourceCode":"    total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))\n    frames = []\n\n    num_frames = num_frames if num_frames > 0 else total_frames\n    frame_indices = _sample_frame_indices(total_frames, num_frames)\n    for idx in range(total_frames):\n        ok = cap.grab()  # next img\n        if not ok:\n            break\n        if idx in frame_indices:  # only decompress needed\n            ret, frame = cap.retrieve()\n            if ret:\n                # OpenCV uses BGR format, we need to convert it to RGB\n                # for PIL and transformers compatibility\n                frames.append(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))\n\n    frames = np.stack(frames)\n    if len(frames) < num_frames:\n        raise ValueError(\n            f\"Could not read enough frames from video file {path}\"\n            f\" (expected {num_frames} frames, got {len(frames)})\"\n        )\n    return frames\n\n\ndef video_to_pil_images_list(path: str, num_frames: int = -1) -> list[Image.Image]:\n    frames = video_to_ndarrays(path, num_frames)\n    return [Image.fromarray(frame) for frame in frames]\n\n\ndef video_get_metadata(path: str, num_frames: int = -1) -> dict[str, Any]:\n    import cv2\n\n    cap = cv2.VideoCapture(path)\n    if not cap.isOpened():\n        raise ValueError(f\"Could not open video file {path}\")\n","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/vllm-project/vllm/blob/c794754062d49a8fdb63ab3c5215b488b865030c/vllm/assets/video.py#L50-L86","documentation":"video_to_ndarrays() grabs frames sequentially and only decodes those in the sampled index set; if the stream ends early (cap.grab() fails) or retrieve() fails on sampled indices, fewer frames than the requested num_frames are collected. After np.stack, the count is checked and ValueError reports expected vs. obtained.","triggerScenarios":"Calling video_to_ndarrays(path, num_frames=N) where N exceeds frames actually decodable: truncated file, CAP_PROP_FRAME_COUNT lying (common with variable-frame-rate or streamed files), corrupted frames mid-file, or cv2codec partially failing.","commonSituations":"Asking for more frames than the clip contains (num_frames > total but metadata reported more), damaged user uploads, partially downloaded datasets where the tail is missing.","solutions":["Read total_frames via video_get_metadata first and clamp num_frames = min(num_frames, total_frames)","Re-download or re-encode the video if it is truncated (verify with ffprobe -count_frames)","Handle the ValueError per-item in a batch multimodal pipeline and skip/fallback that request"],"exampleFix":"# before\nframes = video_to_ndarrays(path, num_frames=64)  # clip only has 50 decodable frames\n# after\nmeta = video_get_metadata(path, num_frames=64)\nframes = video_to_ndarrays(path, num_frames=meta[\"total_num_frames\"])","handlingStrategy":"validation","validationCode":"meta = video_get_metadata(path, num_frames)\nactual = meta[\"total_num_frames\"]\nif num_frames > actual:\n    num_frames = actual  # clamp to decodable frame budget","typeGuard":null,"tryCatchPattern":"try:\n    frames = video_to_ndarrays(path, num_frames)\nexcept ValueError as e:\n    if \"Could not read enough frames\" in str(e):\n        frames = video_to_ndarrays(path, -1)  # take all frames\n    else:\n        raise","preventionTips":["Always clamp num_frames against metadata before decoding","Per-item try/except in multimodal batch serving so one bad video fails one request, not the batch"],"tags":["video","multimodal","opencv","data-quality","validation"],"backgroundTag":null,"analyzedSha":"c794754062d49a8fdb63ab3c5215b488b865030c","analyzedAt":"2026-08-14T21:17:39.825Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}