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 = None

View on GitHub (pinned to 1c6d8d45b3)

Solutions

  1. Re-encode the pose latents so they cover exactly the generation frames (video frames minus the reference slot).
  2. Keep the pose clip's effective frame count in sync with length/slip settings used for generation.
  3. 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

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


AI-assisted analysis of Comfy-Org/ComfyUI@1c6d8d45b3 (2026-08-14). Data as JSON: /api/errors/cfaf7cc1353f4c0c. Report an issue: GitHub.