huggingface/candle · error
unexpected shape for img {:?}
Error message
unexpected shape for img {:?} What it means
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.
Source
Thrown at candle-transformers/src/models/flux/model.rs:596
}
impl super::WithForward for Flux {
#[allow(clippy::too_many_arguments)]
fn forward(
&self,
img: &Tensor,
img_ids: &Tensor,
txt: &Tensor,
txt_ids: &Tensor,
timesteps: &Tensor,
y: &Tensor,
guidance: Option<&Tensor>,
) -> Result<Tensor> {
if txt.rank() != 3 {
candle::bail!("unexpected shape for txt {:?}", txt.shape())
}
if img.rank() != 3 {
candle::bail!("unexpected shape for img {:?}", img.shape())
}
let dtype = img.dtype();
let pe = {
let ids = Tensor::cat(&[txt_ids, img_ids], 1)?;
ids.apply(&self.pe_embedder)?
};
let mut txt = txt.apply(&self.txt_in)?;
let mut img = img.apply(&self.img_in)?;
let vec_ = timestep_embedding(timesteps, 256, dtype)?.apply(&self.time_in)?;
let vec_ = match (self.guidance_in.as_ref(), guidance) {
(Some(g_in), Some(guidance)) => {
(vec_ + timestep_embedding(guidance, 256, dtype)?.apply(g_in))?
}
_ => vec_,
};
let vec_ = (vec_ + y.apply(&self.vector_in))?;
// Double blocksView on GitHub (pinned to d5fee525bf)
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.
Example fix
// before: raw 4D VAE latents
let img = vae_latents; // (1, 16, 64, 64)
flux.forward(&img, &img_ids, &txt, &txt_ids, &t, &y, None)?;
// after: pack 2x2 patches into sequence of 64-dim tokens
let (b, c, h, w) = vae_latents.dims4()?;
let img = vae_latents.reshape((b, c, h / 2, 2, w / 2, 2))?
.permute((0, 2, 4, 1, 3, 5))? // (b, h/2, w/2, c, 2, 2)
.flatten_from(3)? // (b, h/2, w/2, c*2*2 = 64)
.flatten(1, 2)?; // (b, seq, 64)
flux.forward(&img, &img_ids, &txt, &txt_ids, &t, &y, None)?; Defensive patterns
Strategy: validation
Validate before calling
// Rust: ensure latents are packed to rank 3 before forward
let img = pack_latents(&vae_latents)?; // (b, 16, h, w) -> (b, seq, 64)
if img.rank() != 3 {
candle::bail!("img must be (batch, seq, hidden); got {:?}", img.shape());
} Type guard
fn is_rank3(t: &candle_core::Tensor) -> bool { t.rank() == 3 } Try / catch
match flux.forward(&img, &img_ids, &txt, &txt_ids, &ts, &y, guidance) {
Ok(t) => t,
Err(e) if e.to_string().contains("unexpected shape for img") => {
eprintln!("img shape {:?} not packed; run patchify first", img.shape());
return Err(e);
}
Err(e) => return Err(e),
} Prevention
- 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).
When it happens
Trigger: 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).
Common situations: 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.
Understand the failure class
Background: Tensor shape mismatch errors ("must have shape", "expected shape ... got ..."): when tensor dimensions disagree with what an op or layer was told to expect — this error's family across 6 libraries.
Related errors
- unexpected shape for txt {:?}
- dim {dim} is odd
- {dim} is odd
- unexpected len from chunk {ys:?}
- unexpected len from chunk {ys:?}
AI-assisted analysis of huggingface/candle@d5fee525bf (2026-09-02).
Data as JSON: /api/errors/f34461c9d1dd0b7f.
Report an issue: GitHub.