{"record":{"id":"0d6b26c3aa57cb13","repo":"invoke-ai/InvokeAI","slug":"num-frames-must-satisfy-num-frames-1-4-0-0d6b26","errorCode":null,"errorMessage":"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, ...","messagePattern":"num_frames must satisfy \\(num_frames - 1\\) %% 4 == 0 for the Wan VAE's temporal compression \\(got (.+?)\\)\\. Try 5, 9, 13, \\.\\.\\., 81, 85, \\.\\.\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"invokeai/app/invocations/wan_video_denoise.py","lineNumber":158,"sourceCode":"    def invoke(self, context: InvocationContext) -> LatentsOutput:\n        latents = self._run_diffusion(context)\n        # Keep the 5D shape (B, C, T, H, W) — wan_latents_to_video expects it.\n        latents = latents.detach().to(\"cpu\")\n        name = context.tensors.save(tensor=latents)\n        # LatentsOutput.build uses latents.size()[3] / [2] for width / height.\n        # For 5D the spatial dims are at indices 4 / 3 instead of 3 / 2, so we\n        # call the constructor directly with the actual H/W from the inputs.\n        from invokeai.app.invocations.fields import LatentsField\n\n        return LatentsOutput(\n            latents=LatentsField(latents_name=name, seed=self.seed),\n            width=self.width,\n            height=self.height,\n        )\n\n    def _run_diffusion(self, context: InvocationContext) -> torch.Tensor:\n        if (self.num_frames - 1) % 4 != 0:\n            raise ValueError(\n                f\"num_frames must satisfy (num_frames - 1) %% 4 == 0 for the Wan VAE's temporal \"\n                f\"compression (got {self.num_frames}). Try 5, 9, 13, ..., 81, 85, ...\"\n            )\n\n        device = TorchDevice.choose_torch_device()\n        inference_dtype = TorchDevice.choose_bfloat16_safe_dtype(device)\n\n        variant = _resolve_variant(context, self.transformer)\n        _validate_spatial_dimensions(variant, self.width, self.height)\n        spatial_scale = get_spatial_scale_factor(variant)\n\n        # Reuse the image denoise's scheduler construction so we pick up whatever\n        # scheduler the variant ships with (UniPCMultistepScheduler with the\n        # variant's flow_shift, or whatever an on-disk config specifies).\n        scheduler_builder = WanDenoiseInvocation._build_scheduler  # bound on instance below\n        # Bind a minimal instance to call _build_scheduler — it only reads\n        # self.transformer, which is shape-compatible.\n        proxy = WanDenoiseInvocation.model_construct(","sourceCodeStart":140,"sourceCodeEnd":176,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/app/invocations/wan_video_denoise.py#L140-L176","documentation":"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.","triggerScenarios":"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.","commonSituations":"Choosing frame counts from fps/duration math (2s * 24fps = 48) or defaults copied from non-Wan video pipelines.","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)."],"exampleFix":"// before\nnum_frames = 48  # invalid\n// after\nnum_frames = 49  # 4n+1","handlingStrategy":"validation","validationCode":"def snap_to_wan_frames(n: int) -> int:\n    return 1 if n <= 1 else max(5, ((n - 1) // 4) * 4 + 1)\nnum_frames = snap_to_wan_frames(num_frames)","typeGuard":null,"tryCatchPattern":"try:\n    tensor = denoise._run_diffusion(context)\nexcept ValueError as e:\n    if \"num_frames must satisfy\" in str(e):\n        denoise.num_frames = ((denoise.num_frames - 1) // 4) * 4 + 1\n    else:\n        raise","preventionTips":["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."],"tags":["validation","video","num-frames","wan"],"backgroundTag":"invalid-num-frames-wan-vae","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}