invoke-ai/InvokeAI · error · ValueError
Wan VAE decode produced {num_frames} frames; expected {t_pix
Error message
Wan VAE decode produced {num_frames} frames; expected {t_pixel}. What it means
invoke() verifies that the frame count produced by the Wan VAE decode equals t_pixel, the expected pixel-space temporal length computed from the latent temporal dim and Wan's temporal scaling. A mismatch means the decode's frame math diverged from expectations, so a ValueError reports both actual and expected counts.
Source
Thrown at invokeai/app/invocations/wan_latents_to_video.py:224
writer.close()
else:
# [C=3, T_pixel, H, W] in [-1, 1] (roughly), on CPU.
decoded = vae.decode(latents, return_dict=False)[0][0].cpu()
num_frames = decoded.shape[1]
del latents, latents_mean, latents_std
finally:
# The VAE instance is cached and shared; don't leak tiling into other nodes.
if use_tiling:
vae.disable_tiling()
TorchDevice.empty_cache()
if context.util.is_canceled():
raise CanceledException
if num_frames == 0:
raise ValueError("Wan VAE decode produced zero frames.")
if num_frames != t_pixel:
raise ValueError(f"Wan VAE decode produced {num_frames} frames; expected {t_pixel}.")
height, width = h_pixel, w_pixel
duration = num_frames / float(self.fps)
if decoded is not None:
context.logger.info(
f"Encoding MP4: {num_frames} frames @ {self.fps} fps "
f"({duration:.2f}s) at {width}x{height} via libx264"
)
context.util.signal_progress(f"Encoding MP4 ({num_frames} frames @ {self.fps} fps)")
writer = make_mp4_writer(tmp_path, self.fps)
try:
_write_video_frames(writer, _iter_decoded_frames(decoded), context.util.is_canceled)
finally:
writer.close()
del decoded
TorchDevice.empty_cache()
encoded_bytes = tmp_path.stat().st_sizeView on GitHub (pinned to 0b6a024f2f)
Solutions
- Use frame counts matching Wan's convention (e.g. 4*n+1 frames) so t_pixel matches decode output.
- Verify the VAE/diffusers version's temporal expansion matches the node's expectation.
- Let the upstream Wan denoiser compute the latent T rather than crafting latents manually.
- Log latents.shape and expected t_pixel to diagnose the off-by-N and adjust num_frames.
Example fix
// before latents = denoise(num_frames=10) # decode -> 40 frames, expected 41 video = wan_latents_to_video(latents=latents) # ValueError // after latents = denoise(num_frames=9) # conformant count; decode -> expected frames video = wan_latents_to_video(latents=latents)
Defensive patterns
Strategy: validation
Validate before calling
t_pixel = (latents.shape[2] - 1) * 4 + 1
if num_frames_expected != t_pixel:
raise ValueError(f"frame math off: expect {t_pixel}, config says {num_frames_expected}") Type guard
def frame_count_conformant(n: int) -> bool:
return n >= 1 and (n - 1) % 4 == 0 Try / catch
try:
video = node.invoke(context)
except ValueError as e:
if "frames; expected" in str(e):
adjust_num_frames_to_convention() # use 4k+1 counts
video = node.invoke(context)
else:
raise Prevention
- Stick to Wan's (4k+1) frame-count convention.
- Don't hand-edit latent temporal dims.
- Keep the same Wan generation (2.1 vs 2.2) across denoiser and VAE.
- Compute expected frames from latent T rather than hardcoding.
When it happens
Trigger: Decoding latents where the Wan temporal expansion factor (e.g. 4x plus FirstFrame handling) does not match the computed t_pixel — e.g. non-standard frame counts, a VAE version with different temporal behavior, or manually crafted latents with inconsistent T.
Common situations: Choosing frame counts not conformant to Wan's (4k+1) convention; swapping between Wan 2.1 and 2.2 VAEs with different temporal scaling; hand-edited latent tensors.
Related errors
- Wan VAE decode produced zero frames.
- Clip {i} has {n_frames} frames but the requested transitions
- Video {self.video.video_name} has no decodable frames (probe
- Cannot determine frame count for {self.video.video_name}: pr
- Video {self.video.video_name} has no decodable frames (probe
AI-assisted analysis of invoke-ai/InvokeAI@0b6a024f2f (2026-08-29).
Data as JSON: /api/errors/68b4bd49d98f9bb8.
Report an issue: GitHub.