invoke-ai/InvokeAI · error · ValueError

FLUX.2 PiD decode expected a 32-channel latent from flux2_de

Error message

FLUX.2 PiD decode expected a 32-channel latent from flux2_denoise, got shape {tuple(latents.shape)}. The upstream node must output the unpacked FLUX.2 latent.

What it means

The FLUX.2 PiD decoder expects the unpacked FLUX.2 latent layout with 32 channels, (B, 32, H/8, W/8), as produced by flux2_denoise. It patchifies it internally to the packed (B, 128, H/16, W/16) form. A latent with any other channel count means the wrong tensor was fed to the decode node.

Source

Thrown at invokeai/app/invocations/flux2_pid_decode.py:140

    @torch.no_grad()
    def invoke(self, context: InvocationContext) -> ImageOutput:
        # Fail fast if the connected decoder is for a different backbone (the base-agnostic loader lets
        # the Nodes editor wire any PiD decoder into this FLUX.2-specific node).
        assert_pid_decoder_matches_base(
            context.models.get_config(self.pid_decoder.decoder).base,
            BaseModelType.Flux2,
            node_title="FLUX.2 PiD Decode",
        )

        latents = context.tensors.load(self.latents.latents_name)

        # 1) Patchify the stored FLUX.2 latent into PiD's expected layout.
        #    flux2_denoise stores an unpacked (B, 32, H/8, W/8) latent; PiD's
        #    FLUX.2 backbone wants the packed (B, 128, H/16, W/16) form (32*4=128
        #    channels, spatial halved). This mirrors pack_flux2's 2x2 patchify but
        #    keeps a spatial (B, C, h, w) layout rather than a (B, seq, C) sequence.
        if latents.shape[-3] != 32:
            raise ValueError(
                f"FLUX.2 PiD decode expected a 32-channel latent from flux2_denoise, got shape "
                f"{tuple(latents.shape)}. The upstream node must output the unpacked FLUX.2 latent."
            )
        packed = rearrange(latents, "b c (h ph) (w pw) -> b (c ph pw) h w", ph=2, pw=2)
        context.logger.info(
            f"FLUX.2 PiD decode: stored latent shape={tuple(latents.shape)} -> packed for PiD "
            f"shape={tuple(packed.shape)} (expect [B, 128, H/16, W/16]) dtype={packed.dtype}"
        )

        # 2) Resolve the scalar scaling/shift (identity for current FLUX.2 VAEs).
        scaling_factor = _FLUX2_VAE_SCALING_FACTOR_FALLBACK
        shift_factor = _FLUX2_VAE_SHIFT_FACTOR_FALLBACK
        if self.vae is not None:
            vae_info = context.models.load(self.vae.vae)
            with vae_info.model_on_device() as (_, vae):
                config = getattr(vae, "config", None)
                if config is not None and hasattr(config, "scaling_factor"):
                    scaling_factor = float(config.scaling_factor)

View on GitHub (pinned to 0b6a024f2f)

Solutions

  1. Connect the latents input directly to the FLUX.2 flux2_denoise output
  2. Do not apply pack_flux2 or any packing/reshaping before this node (packing happens internally)
  3. Verify the upstream denoise node is the FLUX.2 variant, not FLUX.1 or SD
  4. Check no custom nodes alter channel count between denoise and decode

Example fix

// before: pre-packed latent fed to PiD decode
packed = pack_flux2(latents); pid_decode(latents=packed)
// after: raw flux2_denoise output
pid_decode(latents=denoise_output.latents)
Defensive patterns

Strategy: validation

Validate before calling

latents = context.images.get_latents(denoise_output.latents)
if latents.latents.shape[-3] != 32:
    raise ValueError(f'Expected 32-channel FLUX.2 latent, got {latents.latents.shape}')

Type guard

def is_unpacked_flux2_latent(latents: torch.Tensor) -> bool:
    return latents.ndim == 4 and latents.shape[-3] == 32

Try / catch

try:
    result = pid_decode.invoke(context)
except ValueError as e:
    if '32-channel latent' in str(e):
        reroute_from_flux2_denoise_output()
    raise

Prevention

When it happens

Trigger: latents.shape[-3] != 32 in invoke; connecting a node that outputs a packed/seq-layout FLUX.2 latent (128 channels or sequence form) or a latent from a different model family directly into the PiD decode invocation's latents input.

Common situations: Wiring flux2_denoise output through a reshaping node first; using a generic VAE decode output as input; pipeline graphs copied from FLUX.1 where latent channels differ; manually constructing latents in a custom script.

Related errors


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