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 blocks

View on GitHub (pinned to d5fee525bf)

Solutions

  1. 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.
  2. If latents are unbatched, add `.unsqueeze(0)`.
  3. Verify img_ids is built to match the packed seq_len so positional embedding concatenation stays consistent.
  4. 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

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


AI-assisted analysis of huggingface/candle@d5fee525bf (2026-09-02). Data as JSON: /api/errors/f34461c9d1dd0b7f. Report an issue: GitHub.