Comfy-Org/ComfyUI · error · ValueError
pose branch has {} latent frames, expected {} (generation fr
Error message
pose branch has {} latent frames, expected {} (generation frames minus the reference-image slot) What it means
Wan Animate 2's forward expects pose latents to cover exactly the generation frames minus the reference-image slot (f_gen - 1), because frame 0 of the main latent is the reference slot. A mismatch means the pose conditioning was generated for a different frame count than the video being denoised; the check runs before the animate2 cache select so the cache never keys a rejected latent set.
Source
Thrown at comfy/ldm/wan/model_animate2.py:293
w_patches = (w + (self.patch_size[2] // 2)) // self.patch_size[2]
freqs_pose = self.rope_encode_pose(pose_latents.shape[2], h, w, w_patches, device=x.device, dtype=x.dtype)
return self.forward_orig(x, timestep, context, clip_fea=clip_fea, freqs=freqs, freqs_pose=freqs_pose, pose_latents=pose_latents,
clip_fea_pose=clip_fea_pose, context_pose=context_pose, transformer_options=transformer_options, **kwargs)[:, :, :t, :h, :w]
def forward_orig(self, x, t, context, clip_fea=None, freqs=None, freqs_pose=None, pose_latents=None, clip_fea_pose=None, context_pose=None, pose_strength=1.0, reference_strength=1.0, transformer_options={}, **kwargs):
x_input = x[:, :, 1:] # video-only: frame 0 is the reference slot, offset past it below
x = self.patch_embedding(x.float()).to(x.dtype)
grid_sizes = x.shape[2:]
transformer_options["grid_sizes"] = grid_sizes
f_gen, gh, gw = grid_sizes
hw = gh * gw
x = x.flatten(2).transpose(1, 2)
# the node windows the pose influence via cond timestep ranges: outside the window the cond carries no pose latents, and the branch, its cache traffic and the per-frame attention loop are all skipped
apply_pose = pose_latents is not None
if apply_pose and pose_latents.shape[2] != f_gen - 1: # before cache.select, which would otherwise keep an empty slot keyed to the rejected latents
raise ValueError("pose branch has {} latent frames, expected {} (generation frames minus the reference-image slot)".format(pose_latents.shape[2], f_gen - 1))
cache = transformer_options.get("animate2_cache", None) if apply_pose else None
if cache is not None:
cache.select(pose_latents)
cached = cache is not None and cache.filled(len(self.blocks))
x_pose = None
if not cached and apply_pose:
# 36ch = [latents(16) | mask(4) | latents(16)]; latents twice, and the mask is all ones since every pose frame is known
x_pose = self.patch_embedding(torch.cat([pose_latents, torch.ones_like(pose_latents[:, :4]), pose_latents], dim=1).float()).to(x.dtype)
x_pose = x_pose.flatten(2).transpose(1, 2)
# time embeddings
e = self.time_embedding(sinusoidal_embedding_1d(self.freq_dim, t.flatten()).to(dtype=x.dtype))
e = e.reshape(t.shape[0], -1, e.shape[-1])
e0 = self.time_projection(e).unflatten(2, (6, self.dim))
e0_pose = NoneView on GitHub (pinned to 1c6d8d45b3)
Solutions
- Re-encode the pose latents so they cover exactly the generation frames (video frames minus the reference slot).
- Keep the pose clip's effective frame count in sync with length/slip settings used for generation.
- Outside a pose cond window, omit pose_latents entirely (pass None) rather than a mismatched tensor.
Example fix
# before pose_latents = encode_pose(pose_clip) # encoded for old length out = model(x, t, ctx, pose_latents=pose_latents) # x implies different f_gen # after pose_latents = encode_pose(pose_clip_for_current_length) # T == f_gen - 1 out = model(x, t, ctx, pose_latents=pose_latents)
Defensive patterns
Strategy: validation
Validate before calling
f_gen, gh, gw = grid_sizes # or compute expected count from video length
if pose_latents is not None and pose_latents.shape[2] != f_gen - 1:
raise ValueError(f"pose frames {pose_latents.shape[2]} != generation frames {f_gen - 1}; re-encode pose") Type guard
def pose_matches_generation(pose_latents, f_gen) -> bool:
return pose_latents is None or pose_latents.shape[2] == f_gen - 1 Prevention
- Re-encode pose latents whenever length, slip, or reference-frame settings change.
- Pass pose_latents=None outside the pose conditioning window instead of stale tensors.
When it happens
Trigger: Calling the model with pose_latents whose shape[2] != f_gen - 1 — e.g. pose video has a different length/slip than the generation video, the reference frame was not accounted for, or a length/slip change was made after the pose latents were encoded.
Common situations: Changing the generation length or the last-frame setting without re-encoding the pose branch; pose video frame count not matching (generation frames - 1); stale cached pose latents from an earlier config.
Related errors
- PixDiT_T2I requires context (text embeddings) of shape [B, L
- SeedVR2 conditioning shape must match latent batch/temporal/
- Need at least {require_count} hooks to combine, but only had
- Input img and txt tensors must have 3 dimensions.
- Input txt tensors must have 3 dimensions.
AI-assisted analysis of Comfy-Org/ComfyUI@1c6d8d45b3 (2026-08-14).
Data as JSON: /api/errors/cfaf7cc1353f4c0c.
Report an issue: GitHub.