unslothai/unsloth · error · ValueError
num_frames must be of the form {H3_FRAMES_PER_CHUNK} * n + {
Error message
num_frames must be of the form {H3_FRAMES_PER_CHUNK} * n + {H3_LATENTS_PER_CHUNK}, got {num_frames}. What it means
Raised by h3_video_latent_frames() when num_frames is not congruent to H3_LATENTS_PER_CHUNK (5) modulo H3_FRAMES_PER_CHUNK (17) — i.e. not of the form 17*n + 5. The video VAE only encodes aligned frame counts, so computing latent frames for any other count is undefined. Use h3_align_num_frames() first to snap the count up.
Source
Thrown at studio/backend/core/training/diffusion_h3_clips.py:99
H3_TRAIN_NUM_FRAMES = H3_FRAMES_PER_CHUNK + H3_LATENTS_PER_CHUNK
_VIDEO_EXTS = {".mp4", ".mov", ".mkv", ".webm", ".m4v", ".avi"}
_CAPTION_EXTS = (".txt", ".caption")
def h3_align_num_frames(num_frames: int) -> int:
"""Snap a frame count UP to the next ``17 * n + 5`` the video VAE can encode."""
if num_frames < 1:
raise ValueError(f"num_frames must be positive, got {num_frames}.")
while num_frames % H3_FRAMES_PER_CHUNK != H3_LATENTS_PER_CHUNK:
num_frames += 1
return num_frames
def h3_video_latent_frames(num_frames: int) -> int:
"""Latent frames the video VAE produces for an aligned frame count: ``5 * n + 2``."""
if num_frames % H3_FRAMES_PER_CHUNK != H3_LATENTS_PER_CHUNK:
raise ValueError(
f"num_frames must be of the form {H3_FRAMES_PER_CHUNK} * n + {H3_LATENTS_PER_CHUNK}, "
f"got {num_frames}."
)
return (num_frames - H3_LATENTS_PER_CHUNK) // H3_FRAMES_PER_CHUNK * H3_LATENTS_PER_CHUNK + 2
def h3_audio_latent_count(num_frames: int) -> int:
"""Audio latents (per channel) covering ``num_frames`` frames at 24 fps / 40 latents per s."""
return int(round(num_frames / H3_FPS * H3_AUDIO_LATENTS_PER_SECOND))
def h3_audio_sample_count(num_frames: int) -> int:
"""Waveform samples per channel the audio VAE must be handed for ``num_frames`` frames.
The audio VAE hops 800 samples (32 kHz / 40 latents per second) and right-pads a short
tail, so handing it exactly ``latents * hop`` samples produces exactly the latent count the
packed layout reserves rows for -- no pad, no truncation."""
hop = H3_AUDIO_SAMPLING_RATE // H3_AUDIO_LATENTS_PER_SECONDView on GitHub (pinned to 203007d190)
Solutions
- Run the count through h3_align_num_frames() before h3_video_latent_frames().
- Use H3_TRAIN_NUM_FRAMES (the canonical 22-frame training clip) when in doubt.
- Add a modulo assertion at your call site to catch unaligned counts early.
Example fix
# before latents = h3_video_latent_frames(requested_frames) # e.g. 30 -> ValueError # after latents = h3_video_latent_frames(h3_align_num_frames(requested_frames))
Defensive patterns
Strategy: validation
Validate before calling
def frames_aligned(num_frames: int, per_chunk: int = 17, latents: int = 5) -> bool:
return num_frames % per_chunk == latents Prevention
- Always run h3_align_num_frames() on raw frame counts before latent math.
- Assert the 17*n+5 invariant in tests that touch frame/latent sizing.
When it happens
Trigger: Calling h3_video_latent_frames(30) or any count where num_frames % 17 != 5; passing a raw user-requested duration in frames that was never run through h3_align_num_frames().
Common situations: Computing memory/latent budgets from an arbitrary fps*seconds product; wiring a UI slider straight into latent math; copy-pasting a frame count from another model family (e.g. 24 or 25 fps clips).
Related errors
- num_frames must be positive, got {num_frames}.
- transformer_quant '{requested_scheme}' is unavailable for '{
- '{Path(gguf_filename or '').name}' is the {picked} partition
- '{fam.name}' is a dual-expert model: a single {kind} file co
- The source image has no usable aspect ratio ({aspect_width}x
AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15).
Data as JSON: /api/errors/cc77bc8803d7e440.
Report an issue: GitHub.