{"record":{"id":"3586d8f188616eed","repo":"invoke-ai/InvokeAI","slug":"input-img-and-txt-tensors-must-have-3-dimensions","errorCode":null,"errorMessage":"Input img and txt tensors must have 3 dimensions.","messagePattern":"Input img and txt tensors must have 3 dimensions\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"invokeai/backend/flux/controlnet/instantx_controlnet_flux.py","lineNumber":120,"sourceCode":"            self.is_union = True\n            self.controlnet_mode_embedder = nn.Embedding(num_control_modes, self.hidden_size)\n\n        self.controlnet_x_embedder = zero_module(torch.nn.Linear(self.in_channels, self.hidden_size))\n\n    def forward(\n        self,\n        controlnet_cond: torch.Tensor,\n        controlnet_mode: torch.Tensor | None,\n        img: torch.Tensor,\n        img_ids: torch.Tensor,\n        txt: torch.Tensor,\n        txt_ids: torch.Tensor,\n        timesteps: torch.Tensor,\n        y: torch.Tensor,\n        guidance: torch.Tensor | None = None,\n    ) -> InstantXControlNetFluxOutput:\n        if img.ndim != 3 or txt.ndim != 3:\n            raise ValueError(\"Input img and txt tensors must have 3 dimensions.\")\n\n        img = self.img_in(img)\n\n        # Add controlnet_cond embedding.\n        img = img + self.controlnet_x_embedder(controlnet_cond)\n\n        vec = self.time_in(timestep_embedding(timesteps, 256))\n        if self.params.guidance_embed:\n            if guidance is None:\n                raise ValueError(\"Didn't get guidance strength for guidance distilled model.\")\n            vec = vec + self.guidance_in(timestep_embedding(guidance, 256))\n        vec = vec + self.vector_in(y)\n        txt = self.txt_in(txt)\n\n        # If this is a union ControlNet, then concat the control mode embedding to the T5 text embedding.\n        if self.is_union:\n            if controlnet_mode is None:\n                # We allow users to enter 'None' as the controlnet_mode if they don't want to worry about this input.","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/backend/flux/controlnet/instantx_controlnet_flux.py#L102-L138","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Ensure img has shape (batch, seq_len, hidden) — pack latents e.g. via rearrange/flatten of the patchified grid before calling forward.","Ensure txt has shape (batch, t5_seq_len, 4096); do not squeeze the sequence dim even for batch size 1.","Call img.ndim / txt.ndim (or assert both == 3) at the call site to catch shape bugs early.","Use the pipeline's existing packing utilities instead of hand-reshaping latents."],"exampleFix":"// before\nimg = latents            # (B, 16, H, W)\nout = controlnet(img=img, txt=text_emb.squeeze(0), ...)\n// after\nimg = rearrange(latents, \"b c (h ph) (w pw) -> b (h w) (c ph pw)\", ph=2, pw=2)  # (B, seq, dim)\nout = controlnet(img=img, txt=text_emb, ...)  # keep (B, seq, 4096)","handlingStrategy":"validation","validationCode":"assert img.ndim == 3, f\"img must be (B, seq, dim), got shape {tuple(img.shape)}\"\nassert txt.ndim == 3, f\"txt must be (B, seq, 4096), got shape {tuple(txt.shape)}\"","typeGuard":"def is_seq_tokens(t: torch.Tensor) -> bool:\n    return t.ndim == 3","tryCatchPattern":"try:\n    out = controlnet(img=img, txt=txt, ...)\nexcept ValueError as e:\n    if \"must have 3 dimensions\" in str(e):\n        raise RuntimeError(f\"bad shapes: img={tuple(img.shape)}, txt={tuple(txt.shape)}; pack latents to (B,seq,dim) first\") from e\n    raise","preventionTips":["Always patchify latents (2x2 rearrange) before passing to Flux ControlNet.","Never .squeeze() text embeddings on the sequence dimension.","Check tensor .shape at the call boundary; log it when debugging pipelines."],"tags":["tensor-shape","validation","flux","controlnet"],"backgroundTag":"tensor-rank-mismatch","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}