diff --git a/tests/multimodal/test_video.py b/tests/multimodal/test_video.py index 9c24b5f96932..14f9df05e017 100644 --- a/tests/multimodal/test_video.py +++ b/tests/multimodal/test_video.py @@ -1755,3 +1755,16 @@ def test_glm5next_read_frames_dense_walk_matches_stock(tmp_path): assert abs(round(float(np.asarray(frame).mean())) - idx) <= 1 # One initial seek, then pure walking -- no re-seek churn. assert cap.seeks == 1 + + +def test_glmga_video_backend_rejects_unknown_source_fps(): + """A container reporting 0 fps (VFR/unknown) must raise a clear + ValueError instead of ZeroDivisionError.""" + target = VideoTargetMetadata(num_frames=-1, fps=2, max_duration=300) + # Duration may or may not be reported; either path divides by original_fps. + for duration in (5.0, 0.0): + source = VideoSourceMetadata( + total_frames_num=150, original_fps=0.0, duration=duration + ) + with pytest.raises(ValueError, match="unknown frame rate"): + GLMGAVideoBackend.compute_frames_index_to_sample(source, target) diff --git a/vllm/multimodal/video.py b/vllm/multimodal/video.py index 2cc2aa98877a..cf1ade3f8392 100644 --- a/vllm/multimodal/video.py +++ b/vllm/multimodal/video.py @@ -813,6 +813,14 @@ def compute_frames_index_to_sample( max_frame_idx = source.total_frames_num - 1 max_frames = min(kwargs.get("max_frames", cls._MAX_FRAMES), cls._MAX_FRAMES) + # vLLM reports original_fps == 0 for clips with unknown/variable fps + # (VFR, malformed, streaming); fail loudly instead of dividing by zero. + if original_fps <= 0: + raise ValueError( + "GLMGA video sampling needs a known source fps, but the " + "container reported 0 (variable or unknown frame rate)." + ) + duration = duration or round(max_frame_idx / original_fps) + 1 extract_t = int(duration * target_fps)