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
Flux ControlNet's __call__ requires img (latent image tokens) and txt (text tokens) to be rank-3 tensors of shape (batch, sequence, channel). Anything else raises ValueError immediately. This contract matches the packed-token format produced by Flux latents and T5 text encodings before entering the controlnet.
Source
Thrown at comfy/ldm/flux/controlnet.py:122
operations.Conv2d(16, 16, 3, padding=1, stride=2, dtype=dtype, device=device),
nn.SiLU(),
operations.Conv2d(16, 16, 3, padding=1, dtype=dtype, device=device)
)
def forward_orig(
self,
img: Tensor,
img_ids: Tensor,
controlnet_cond: Tensor,
txt: Tensor,
txt_ids: Tensor,
timesteps: Tensor,
y: Tensor,
guidance: Tensor = None,
control_type: Tensor = None,
) -> Tensor:
if img.ndim != 3 or txt.ndim != 3:
raise ValueError("Input img and txt tensors must have 3 dimensions.")
if y is None:
y = torch.zeros((img.shape[0], self.params.vec_in_dim), device=img.device, dtype=img.dtype)
else:
y = y[:, :self.params.vec_in_dim]
# running on sequences img
img = self.img_in(img)
controlnet_cond = self.pos_embed_input(controlnet_cond)
img = img + controlnet_cond
vec = self.time_in(timestep_embedding(timesteps, 256))
if self.params.guidance_embed:
vec = vec + self.guidance_in(timestep_embedding(guidance, 256))
vec = vec + self.vector_in(y)
txt = self.txt_in(txt)
if self.controlnet_mode_embedder is not None and len(control_type) > 0:View on GitHub (pinned to 1c6d8d45b3)
Solutions
- Feed the same packed (B, seq, dim) img/txt tensors that the main Flux model consumes (from Flux latent conditioning, not raw VAE latents).
- If your tensor is (B,C,H,W), apply the Flux patchify step first to reach rank 3.
- Use the stock Flux controlnet ComfyUI nodes, which build correctly shaped inputs, instead of calling the module directly.
Example fix
# before (raw VAE latent, rank 4) ctrl = controlnet(img_latent_bchw, img_ids, cond, txt_btd, txt_ids, t, y) # after (packed sequence, rank 3) img_seq = pack_latents(img_latent_bchw) # -> (B, H/2*W/2, C*4) ctrl = controlnet(img_seq, img_ids, cond, txt_btd, txt_ids, t, y)
Defensive patterns
Strategy: validation
Validate before calling
assert img.ndim == 3 and txt.ndim == 3, f"img/txt must be (B,seq,dim); got img {tuple(img.shape)}, txt {tuple(txt.shape)}" Type guard
def is_flux_sequence(t: "torch.Tensor") -> bool:
return t.ndim == 3 Prevention
- Always pass the packed sequence latents produced by the Flux conditioning path, never raw VAE latents.
- Prefer stock Flux controlnet nodes over direct module calls.
- Unsqueeze single samples to restore the batch dim.
When it happens
Trigger: Passing a 4D image tensor (B,C,H,W) that was not patchified/packed into (B,seq,dim); passing text embeddings with an extra batch dim; reusing a controlnet wrapper with tensors formatted for a different conditioning path.
Common situations: Custom nodes calling the Flux controlnet forward directly with VAE latents instead of Flux sequence latents; importing code from a diffusers-style pipeline where tensor layouts differ; empty or scalar edge-case tensors.
Related errors
- Input img and txt tensors must have 3 dimensions.
- The current maximum number of supported images is 9.
- Control type {max_type_name}({max_type}) is out of range for
- y is None, did you try using a controlnet for SDXL on SD1?
- This Controlnet needs a VAE but none was provided, please us
AI-assisted analysis of Comfy-Org/ComfyUI@1c6d8d45b3 (2026-08-14).
Data as JSON: /api/errors/1642b759c96e2c04.
Report an issue: GitHub.