invoke-ai/InvokeAI · error · ValueError
num_frames must satisfy (num_frames - 1) %% 4 == 0 for the W
Error message
num_frames must satisfy (num_frames - 1) %% 4 == 0 for the Wan VAE's temporal compression (got {self.num_frames}). Try 5, 9, 13, ..., 81, 85, ... What it means
The Wan Video Denoise invocation enforces the same Wan VAE temporal-compression constraint as the encoder: (num_frames - 1) must be divisible by 4 so latents line up on the time axis. The check runs at the start of _run_diffusion so users fail fast with a clear message instead of a shape mismatch during denoising or decode.
Source
Thrown at invokeai/app/invocations/wan_video_denoise.py:158
def invoke(self, context: InvocationContext) -> LatentsOutput:
latents = self._run_diffusion(context)
# Keep the 5D shape (B, C, T, H, W) — wan_latents_to_video expects it.
latents = latents.detach().to("cpu")
name = context.tensors.save(tensor=latents)
# LatentsOutput.build uses latents.size()[3] / [2] for width / height.
# For 5D the spatial dims are at indices 4 / 3 instead of 3 / 2, so we
# call the constructor directly with the actual H/W from the inputs.
from invokeai.app.invocations.fields import LatentsField
return LatentsOutput(
latents=LatentsField(latents_name=name, seed=self.seed),
width=self.width,
height=self.height,
)
def _run_diffusion(self, context: InvocationContext) -> torch.Tensor:
if (self.num_frames - 1) % 4 != 0:
raise ValueError(
f"num_frames must satisfy (num_frames - 1) %% 4 == 0 for the Wan VAE's temporal "
f"compression (got {self.num_frames}). Try 5, 9, 13, ..., 81, 85, ..."
)
device = TorchDevice.choose_torch_device()
inference_dtype = TorchDevice.choose_bfloat16_safe_dtype(device)
variant = _resolve_variant(context, self.transformer)
_validate_spatial_dimensions(variant, self.width, self.height)
spatial_scale = get_spatial_scale_factor(variant)
# Reuse the image denoise's scheduler construction so we pick up whatever
# scheduler the variant ships with (UniPCMultistepScheduler with the
# variant's flow_shift, or whatever an on-disk config specifies).
scheduler_builder = WanDenoiseInvocation._build_scheduler # bound on instance below
# Bind a minimal instance to call _build_scheduler — it only reads
# self.transformer, which is shape-compatible.
proxy = WanDenoiseInvocation.model_construct(View on GitHub (pinned to 0b6a024f2f)
Solutions
- Set num_frames to a 4n+1 value (5, 9, 13, ..., 81, 85).
- Adjust duration math: e.g. ~2s at 24fps -> 49 frames instead of 48.
- Keep num_frames = 1 only for single-image workflows; the check passes for 1 ((1-1)%4==0).
Example fix
// before num_frames = 48 # invalid // after num_frames = 49 # 4n+1
Defensive patterns
Strategy: validation
Validate before calling
def snap_to_wan_frames(n: int) -> int:
return 1 if n <= 1 else max(5, ((n - 1) // 4) * 4 + 1)
num_frames = snap_to_wan_frames(num_frames) Try / catch
try:
tensor = denoise._run_diffusion(context)
except ValueError as e:
if "num_frames must satisfy" in str(e):
denoise.num_frames = ((denoise.num_frames - 1) // 4) * 4 + 1
else:
raise Prevention
- Enforce 4n+1 frame counts wherever num_frames is set.
- Compute clip length as frames-first, then derive seconds.
- Reuse the same num_frames primitive across encoder and denoise nodes.
When it happens
Trigger: Setting num_frames on the 'Wan Video Denoise' invocation to any value > 1 that is not of the form 4n+1, e.g. 33, 48, 100.
Common situations: Choosing frame counts from fps/duration math (2s * 24fps = 48) or defaults copied from non-Wan video pipelines.
Related errors
- num_frames must satisfy (num_frames - 1) %% 4 == 0 for the W
- Wan latents-to-video requires batch size 1; got {latents.sha
- Wan latents-to-video expects a 5D latent tensor [B, C, T, H,
- Wan latents-to-video requires non-empty temporal and spatial
- Reference-image num_frames ({self.ref_image.num_frames}) mus
AI-assisted analysis of invoke-ai/InvokeAI@0b6a024f2f (2026-08-29).
Data as JSON: /api/errors/0d6b26c3aa57cb13.
Report an issue: GitHub.