Comfy-Org/ComfyUI · 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

The main Flux forward() enforces the same token-sequence contract as the controlnet: img and txt must be rank-3 (batch, sequence, channel) tensors. Raw 4D image latents, unbatched 2D tensors, or mis-shaped conditioning raise ValueError before any compute. This mirrors the packed-token format Flux uses end to end.

Source

Thrown at comfy/ldm/flux/model.py:166

        self,
        img: Tensor,
        img_ids: Tensor,
        txt: Tensor,
        txt_ids: Tensor,
        timesteps: Tensor,
        y: Tensor,
        guidance: Tensor = None,
        control = None,
        timestep_zero_index=None,
        transformer_options={},
        attn_mask: Tensor = None,
    ) -> Tensor:

        transformer_options = transformer_options.copy()
        patches = transformer_options.get("patches", {})
        patches_replace = transformer_options.get("patches_replace", {})
        if img.ndim != 3 or txt.ndim != 3:
            raise ValueError("Input img and txt tensors must have 3 dimensions.")

        # running on sequences img
        img = self.img_in(img)
        vec = self.time_in(timestep_embedding(timesteps, 256).to(img.dtype))
        if self.params.guidance_embed:
            if guidance is not None:
                vec = vec + self.guidance_in(timestep_embedding(guidance, 256).to(img.dtype))

        if self.vector_in is not None:
            if y is None:
                y = torch.zeros((img.shape[0], self.params.vec_in_dim), device=img.device, dtype=img.dtype)
            vec = vec + self.vector_in(y[:, :self.params.vec_in_dim])

        if self.txt_norm is not None:
            txt = self.txt_norm(txt)
        txt = self.txt_in(txt)

        if "post_input" in patches:

View on GitHub (pinned to 1c6d8d45b3)

Solutions

  1. Pass Flux sequence latents (B, seq, dim) — in ComfyUI flows these come from the Flux model's own latent packing, not from a VAE decode/encode round trip.
  2. If writing a custom patch that touches x, return tensors with the same rank you received.
  3. Add a batch dimension to single samples: txt = txt.unsqueeze(0), img = img.unsqueeze(0).

Example fix

# before
noise_pred = model(x_bchw, t, context=txt_2d)

# after
x_seq = pack(x_bchw)          # (B, seq, dim)
txt = txt_2d.unsqueeze(0)     # (1, seq, dim)
noise_pred = model(x_seq, t, context=txt)
Defensive patterns

Strategy: validation

Validate before calling

assert img.ndim == 3 and txt.ndim == 3, f"Flux forward needs (B,seq,dim); img ndim={img.ndim}, txt ndim={txt.ndim}"

Type guard

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

Prevention

When it happens

Trigger: Calling model.forward() (or a sampler invoking it) with img of shape (B,C,H,W) instead of packed (B,seq,dim); txt embeddings of rank 2 (missing batch dim); tensors flattened incorrectly by a custom patch or wrapper.

Common situations: Custom sampling loops that bypass ComfyUI's model patcher and pass VAE latents directly; transformer patches (patches_replace) that reshape x and return the wrong rank; debugging code that feeds a single image without a batch dim.

Related errors


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