{"record":{"id":"96ffb892de968e9e","repo":"unslothai/unsloth","slug":"path-path-name-decoded-to-len-frames-frames","errorCode":null,"errorMessage":"{Path(path).name} decoded to {len(frames)} frames at {H3_FPS} fps, but a training clip needs {num_frames} ({num_frames / H3_FPS:.2f}s). Use longer clips.","messagePattern":"(.+?) decoded to (.+?) frames at (.+?) fps, but a training clip needs (.+?) \\((.+?)s\\)\\. Use longer clips\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"studio/backend/core/training/diffusion_h3_clips.py","lineNumber":330,"sourceCode":"        next_target = 0\n        for source_index, frame in enumerate(container.decode(video = 0)):\n            if len(frames) >= num_frames:\n                break\n            if int(next_target * source_fps / H3_FPS) > source_index:\n                continue\n            image = frame.to_image().convert(\"RGB\")\n            # Before the crop, not after: the canvas is in display orientation, so cropping the\n            # coded frame would trim the wrong pair of edges as well as train it sideways.\n            image = apply_display_rotation(image, display_rotation_degrees(frame, stream), Image)\n            image = _cover_resize(image, width, height, Image)\n            while (\n                int(next_target * source_fps / H3_FPS) <= source_index and len(frames) < num_frames\n            ):\n                frames.append(np.asarray(image, dtype = \"uint8\"))\n                next_target += 1\n\n    if len(frames) < num_frames:\n        raise ValueError(\n            f\"{Path(path).name} decoded to {len(frames)} frames at {H3_FPS} fps, but a training \"\n            f\"clip needs {num_frames} ({num_frames / H3_FPS:.2f}s). Use longer clips.\"\n        )\n    if on_note is not None and source_duration_s > (num_frames / H3_FPS) * 1.05:\n        on_note(\n            f\"{Path(path).name} is {source_duration_s:.1f}s; MiniMax-H3 trains its first \"\n            f\"{num_frames / H3_FPS:.2f}s and its caption is paired with that. Trim the clip to \"\n            f\"the part the caption describes.\"\n        )\n\n    waveform = _decode_clip_audio(path, target_samples, av, np)\n    return np.stack(frames), waveform\n\n\ndef display_rotation_degrees(frame: Any, stream: Any) -> int:\n    \"\"\"The clip's display rotation, one of 0/90/180/270, as a PLAYER would apply it.\n\n    PyAV hands back the CODED frame: unlike the ffmpeg CLI, ``to_image()`` and ``to_ndarray()``","sourceCodeStart":312,"sourceCodeEnd":348,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/core/training/diffusion_h3_clips.py#L312-L348","documentation":"Raised by decode_clip() when, after resampling the source to H3_FPS, fewer than num_frames frames were produced by the end of the file — i.e. the clip is too short for the requested training length. decode_clip trains the first num_frames/H3_FPS seconds, so a shorter source simply cannot fill the window. Longer sources are fine (a note suggests trimming), only short ones fail.","triggerScenarios":"A clip whose decoded duration at H3_FPS yields fewer than num_frames frames; num_frames set to a larger 17*n+5 value than the clip supports (e.g. 39 frames needed but the clip is ~1s); high-fps sources that are actually very brief.","commonSituations":"Short meme/sfx clips (< 1s) mixed into a dataset; training at a longer clip length after initial experiments; mis-detected fps causing under-sampling; teaser/trailer snippets.","solutions":["Use longer source clips — at least num_frames / 24 seconds (e.g. 22 frames ≈ 0.92s, with margin).","Or lower the requested num_frames to the next smaller 17*n+5 value the clips can fill.","Pre-scan durations and drop clips shorter than the training window, reporting them by name."],"exampleFix":"# before\nframes, wave = decode_clip(p, num_frames=39, ...)  # clip is 1.0s (~24 frames)\n\n# after\nframes, wave = decode_clip(p, num_frames=22, ...)  # fits a ~1s clip at 24 fps","handlingStrategy":"validation","validationCode":"H3_FPS = 24\n\ndef clip_long_enough(path: str, num_frames: int) -> bool:\n    import av\n    with av.open(path) as c:\n        s = c.streams.video[0]\n        dur = float(s.duration * s.time_base) if s.duration and s.time_base else 0.0\n    return dur == 0.0 or dur >= num_frames / H3_FPS  # unknown duration -> let decode decide","typeGuard":null,"tryCatchPattern":"try:\n    frames, waveform = decode_clip(p, num_frames=n, width=w, height=h)\nexcept ValueError as e:\n    if \"Use longer clips\" in str(e):\n        skip_and_log(p)\n    else:\n        raise","preventionTips":["Match num_frames (17*n+5) to your shortest clip before launch.","Filter sub-second clips from the dataset during prep."],"tags":["video","frame-count","dataset","minimax-h3"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}