{"record":{"id":"f34461c9d1dd0b7f","repo":"huggingface/candle","slug":"unexpected-shape-for-img","errorCode":null,"errorMessage":"unexpected shape for img {:?}","messagePattern":"unexpected shape for img (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"candle-transformers/src/models/flux/model.rs","lineNumber":596,"sourceCode":"}\n\nimpl super::WithForward for Flux {\n    #[allow(clippy::too_many_arguments)]\n    fn forward(\n        &self,\n        img: &Tensor,\n        img_ids: &Tensor,\n        txt: &Tensor,\n        txt_ids: &Tensor,\n        timesteps: &Tensor,\n        y: &Tensor,\n        guidance: Option<&Tensor>,\n    ) -> Result<Tensor> {\n        if txt.rank() != 3 {\n            candle::bail!(\"unexpected shape for txt {:?}\", txt.shape())\n        }\n        if img.rank() != 3 {\n            candle::bail!(\"unexpected shape for img {:?}\", img.shape())\n        }\n        let dtype = img.dtype();\n        let pe = {\n            let ids = Tensor::cat(&[txt_ids, img_ids], 1)?;\n            ids.apply(&self.pe_embedder)?\n        };\n        let mut txt = txt.apply(&self.txt_in)?;\n        let mut img = img.apply(&self.img_in)?;\n        let vec_ = timestep_embedding(timesteps, 256, dtype)?.apply(&self.time_in)?;\n        let vec_ = match (self.guidance_in.as_ref(), guidance) {\n            (Some(g_in), Some(guidance)) => {\n                (vec_ + timestep_embedding(guidance, 256, dtype)?.apply(g_in))?\n            }\n            _ => vec_,\n        };\n        let vec_ = (vec_ + y.apply(&self.vector_in))?;\n\n        // Double blocks","sourceCodeStart":578,"sourceCodeEnd":614,"githubUrl":"https://github.com/huggingface/candle/blob/d5fee525bfde3273eb7c9b75fd2bc4937be867ca/candle-transformers/src/models/flux/model.rs#L578-L614","documentation":"Flux::forward validates that the image-latent tensor `img` has rank 3 (batch, seq_len_img, hidden) before running the DiT. This bail fires when img has another rank — commonly a 4D latent (batch, channels, h, w) straight from the VAE that was never repacked into the packed sequence layout, or a 2D unbatched tensor. It guards before position embeddings (img_ids + pe_embedder) and img_in projection.","triggerScenarios":"Passing VAE decoder/encoder latents of shape (b, 16, h, w) directly as img without calling the packing/patchify step used in candle's flux example; passing squeezed latents (h, w) with no batch or channel handling; custom pipelines that forget `rearrange`/reshape into (b, seq, channels*patch*patch).","commonSituations":"Adapting candle Flux into an existing pipeline where latents come as 4D conv tensors; mixing up img (packed latents) and img (decoded image) variables; porting diffusers code where FluxTransformerModel does the packing internally.","solutions":["Pack latents to rank 3 first: convert (b, 16, h, w) into (b, seq, 256) per the Flux patchify (2x2 patches, in_channels=64 after packing); mirror the code in candle's flux-main example.","If latents are unbatched, add `.unsqueeze(0)`.","Verify img_ids is built to match the packed seq_len so positional embedding concatenation stays consistent.","Print img.shape() at the call site and compare to (batch, seq_len, cfg.hidden_size) expectations before forward."],"exampleFix":"// before: raw 4D VAE latents\nlet img = vae_latents; // (1, 16, 64, 64)\nflux.forward(&img, &img_ids, &txt, &txt_ids, &t, &y, None)?;\n\n// after: pack 2x2 patches into sequence of 64-dim tokens\nlet (b, c, h, w) = vae_latents.dims4()?;\nlet img = vae_latents.reshape((b, c, h / 2, 2, w / 2, 2))?\n    .permute((0, 2, 4, 1, 3, 5))? // (b, h/2, w/2, c, 2, 2)\n    .flatten_from(3)? // (b, h/2, w/2, c*2*2 = 64)\n    .flatten(1, 2)?;  // (b, seq, 64)\nflux.forward(&img, &img_ids, &txt, &txt_ids, &t, &y, None)?;","handlingStrategy":"validation","validationCode":"// Rust: ensure latents are packed to rank 3 before forward\nlet img = pack_latents(&vae_latents)?; // (b, 16, h, w) -> (b, seq, 64)\nif img.rank() != 3 {\n    candle::bail!(\"img must be (batch, seq, hidden); got {:?}\", img.shape());\n}","typeGuard":"fn is_rank3(t: &candle_core::Tensor) -> bool { t.rank() == 3 }","tryCatchPattern":"match flux.forward(&img, &img_ids, &txt, &txt_ids, &ts, &y, guidance) {\n    Ok(t) => t,\n    Err(e) if e.to_string().contains(\"unexpected shape for img\") => {\n        eprintln!(\"img shape {:?} not packed; run patchify first\", img.shape());\n        return Err(e);\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Never pass 4D conv latents directly to Flux; always pack 2x2 patches into a token sequence first.","Build img_ids to match the packed seq_len so ids and latents stay consistent.","Reuse the packing helpers from candle's flux example instead of reimplementing rearranges.","Add an assertion that img.dims()[2] equals the expected hidden projection (after img_in it becomes hidden_size)."],"tags":["candle","tensor-shape","rank","flux","latent-packing"],"backgroundTag":"tensor-shape-mismatch","analyzedSha":"d5fee525bfde3273eb7c9b75fd2bc4937be867ca","analyzedAt":"2026-09-02T00:15:47.023Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T06:17:21.866Z"}