invoke-ai/InvokeAI · error · ValueError

expected {LATENT_DIM} packed channels, got {channels}

Error message

expected {LATENT_DIM} packed channels, got {channels}

What it means

unpatchify_and_denormalize expects the packed latent tensor to have exactly LATENT_DIM channels; any other channel count cannot be unpatchified back into the 32-channel autoencoder latent space.

Source

Thrown at invokeai/backend/ideogram4/sampling_utils.py:120

    """
    batch_size = z.shape[0]
    z = z.reshape(batch_size, grid_h, grid_w, LATENT_DIM)
    return z.permute(0, 3, 1, 2).contiguous()


def unpatchify_and_denormalize(
    packed: torch.Tensor,
    latent_shift: torch.Tensor,
    latent_scale: torch.Tensor,
) -> torch.Tensor:
    """Convert a packed latent ``(1, LATENT_DIM, grid_h, grid_w)`` to a VAE latent ``(1, 32, H/8, W/8)``.

    Applies the per-channel latent denormalization (``z * scale + shift``) in the
    packed space, then unpatchifies, exactly as ``Ideogram4Pipeline._decode`` does.
    """
    batch_size, channels, grid_h, grid_w = packed.shape
    if channels != LATENT_DIM:
        raise ValueError(f"expected {LATENT_DIM} packed channels, got {channels}")

    # (B, grid_h, grid_w, LATENT_DIM)
    z = packed.permute(0, 2, 3, 1)
    z = z * latent_scale.to(z.device, z.dtype) + latent_shift.to(z.device, z.dtype)

    ae_channels = LATENT_DIM // (PATCH_SIZE * PATCH_SIZE)  # 32
    z = z.reshape(batch_size, grid_h, grid_w, PATCH_SIZE, PATCH_SIZE, ae_channels)
    z = z.permute(0, 5, 1, 3, 2, 4).contiguous()
    z = z.reshape(batch_size, ae_channels, grid_h * PATCH_SIZE, grid_w * PATCH_SIZE)
    return z

View on GitHub (pinned to 0b6a024f2f)

Solutions

  1. Ensure the tensor passed in is the LATENT_DIM-channel packed tensor produced by patchify/pipeline denoise steps
  2. Check tensor.permute/reshape so channels are in dim 1
  3. If starting from a VAE latent, patchify it (pack LATENT_DIM = ae_channels * PATCH_SIZE^2) first

Example fix

// before
unpatchify_and_denormalize(vae_latent, scale, shift)  # 32 channels
// after
packed = patchify(vae_latent)  # -> (B, LATENT_DIM, gh, gw)
unpatchify_and_denormalize(packed, scale, shift)
Defensive patterns

Strategy: type-guard

Validate before calling

assert packed.ndim == 4, f"expected (B,C,gh,gw), got {packed.shape}"
assert packed.shape[1] == LATENT_DIM, f"need {LATENT_DIM} channels, got {packed.shape[1]}"

Type guard

def is_packed_latent(t: torch.Tensor) -> bool:
    return t.ndim == 4 and t.shape[1] == LATENT_DIM

Try / catch

try:
    image = unpatchify_and_denormalize(packed, scale, shift)
except ValueError as e:
    if "packed channels" in str(e):
        packed = patchify(packed)  # or fix permute
        image = unpatchify_and_denormalize(packed, scale, shift)
    else:
        raise

Prevention

When it happens

Trigger: Passing a raw VAE latent (e.g. 16 or 32 channels) or a mis-patched tensor to unpatchify_and_denormalize from step_callback or invoke instead of the LATENT_DIM-channel packed diffusion tensor.

Common situations: Wiring the decoded image callback to the wrong tensor, using latents from another model's VAE, forgetting the patchify step, transposing shape incorrectly so channels dimension is wrong.

Understand the failure class

Background: Tensor shape mismatch errors ("must have shape", "expected shape ... got ..."): when tensor dimensions disagree with what an op or layer was told to expect — this error's family across 6 libraries.

Related errors


AI-assisted analysis of invoke-ai/InvokeAI@0b6a024f2f (2026-08-29). Data as JSON: /api/errors/d15a7f05e158f894. Report an issue: GitHub.