invoke-ai/InvokeAI · error · ValueError

Input img and txt tensors must have 3 dimensions.

Error message

Input img and txt tensors must have 3 dimensions.

What it means

InstantXControlNetFlux.forward operates on sequence-form latent/text tokens, so both img (latent image tokens) and txt (T5 text tokens) must be rank-3 tensors of shape (batch, seq_len, channels). If either is rank-2 (e.g. (B, C) or a flat token row) or rank-4 (unflattened image feature map), the transformer math and attention ID concatenation would be wrong, so it raises immediately.

Source

Thrown at invokeai/backend/flux/controlnet/instantx_controlnet_flux.py:120

            self.is_union = True
            self.controlnet_mode_embedder = nn.Embedding(num_control_modes, self.hidden_size)

        self.controlnet_x_embedder = zero_module(torch.nn.Linear(self.in_channels, self.hidden_size))

    def forward(
        self,
        controlnet_cond: torch.Tensor,
        controlnet_mode: torch.Tensor | None,
        img: torch.Tensor,
        img_ids: torch.Tensor,
        txt: torch.Tensor,
        txt_ids: torch.Tensor,
        timesteps: torch.Tensor,
        y: torch.Tensor,
        guidance: torch.Tensor | None = None,
    ) -> InstantXControlNetFluxOutput:
        if img.ndim != 3 or txt.ndim != 3:
            raise ValueError("Input img and txt tensors must have 3 dimensions.")

        img = self.img_in(img)

        # Add controlnet_cond embedding.
        img = img + self.controlnet_x_embedder(controlnet_cond)

        vec = self.time_in(timestep_embedding(timesteps, 256))
        if self.params.guidance_embed:
            if guidance is None:
                raise ValueError("Didn't get guidance strength for guidance distilled model.")
            vec = vec + self.guidance_in(timestep_embedding(guidance, 256))
        vec = vec + self.vector_in(y)
        txt = self.txt_in(txt)

        # If this is a union ControlNet, then concat the control mode embedding to the T5 text embedding.
        if self.is_union:
            if controlnet_mode is None:
                # We allow users to enter 'None' as the controlnet_mode if they don't want to worry about this input.

View on GitHub (pinned to 0b6a024f2f)

Solutions

  1. Ensure img has shape (batch, seq_len, hidden) — pack latents e.g. via rearrange/flatten of the patchified grid before calling forward.
  2. Ensure txt has shape (batch, t5_seq_len, 4096); do not squeeze the sequence dim even for batch size 1.
  3. Call img.ndim / txt.ndim (or assert both == 3) at the call site to catch shape bugs early.
  4. Use the pipeline's existing packing utilities instead of hand-reshaping latents.

Example fix

// before
img = latents            # (B, 16, H, W)
out = controlnet(img=img, txt=text_emb.squeeze(0), ...)
// after
img = rearrange(latents, "b c (h ph) (w pw) -> b (h w) (c ph pw)", ph=2, pw=2)  # (B, seq, dim)
out = controlnet(img=img, txt=text_emb, ...)  # keep (B, seq, 4096)
Defensive patterns

Strategy: validation

Validate before calling

assert img.ndim == 3, f"img must be (B, seq, dim), got shape {tuple(img.shape)}"
assert txt.ndim == 3, f"txt must be (B, seq, 4096), got shape {tuple(txt.shape)}"

Type guard

def is_seq_tokens(t: torch.Tensor) -> bool:
    return t.ndim == 3

Try / catch

try:
    out = controlnet(img=img, txt=txt, ...)
except ValueError as e:
    if "must have 3 dimensions" in str(e):
        raise RuntimeError(f"bad shapes: img={tuple(img.shape)}, txt={tuple(txt.shape)}; pack latents to (B,seq,dim) first") from e
    raise

Prevention

When it happens

Trigger: Calling forward() with img or txt lacking a sequence dimension — e.g. passing a (B,C,H,W) latent tensor without packing it to (B, H*W, C), or passing a 2D (B,C) text embedding instead of (B, seq, 4096).

Common situations: Feeding raw VAE latents straight from the decoder without patchify/rearrange; squeezing text embeddings to 2D; batch-dim removal with batch size 1 (producing 2D tensors); reusing tensors shaped for diffusers' UNet-style APIs.

Related errors


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