huggingface/candle · error

unsqueeze: maximum size for tensor at dimension {dim} is {ma

Error message

unsqueeze: maximum size for tensor at dimension {dim} is {max_len} but size is {size}

What it means

This is thrown by the unsqueeze-related size validation (the code building sizes/strides for a view with a new dimension). The requested size for the new dimension exceeds the maximum allowed at that position (1 for a scalar/empty tensor, otherwise the existing size at that dim), so the operation cannot be expressed as a view and it bails. Typically this comes from Tensor::broadcast UNSQUEEZE-style expansion (e.g. broadcast_in / expand paths) where a size of N is requested for a dim that has size 1 or doesn't exist.

Source

Thrown at candle-core/src/tensor.rs:2980

        Ok(result)
    }

    /// Returns a view of which contains all slices of size `size` from self tensor in the dimension
    /// `dim` and stepped by `step`.
    pub fn unfold<D: Dim>(&self, dim: D, size: usize, step: usize) -> Result<Self> {
        // https://github.com/pytorch/pytorch/blob/75b0720a97ac5d82e8a7a1a6ae7c5f7a87d7183d/aten/src/ATen/native/TensorShape.cpp#L3785-L3804
        let mut sizes = self.dims().to_vec();
        let mut strides = self.stride().to_vec();

        let dim = dim.to_index(self.shape(), "unfold")?;

        let max_len = if self.dims().is_empty() {
            1
        } else {
            sizes[dim]
        };
        if size > max_len {
            bail!(
                "unsqueeze: maximum size for tensor at dimension {dim} is {max_len} but size is {size}"
            )
        }
        sizes.push(size);
        strides.push(if self.dims().is_empty() {
            1
        } else {
            strides[dim]
        });

        if !self.dims().is_empty() {
            sizes[dim] = ((sizes[dim] as f32 - size as f32) / step as f32 + 1.) as usize;
            strides[dim] *= step;
        }

        let tensor_ = Tensor_ {
            id: TensorId::new(),
            storage: self.storage.clone(),

View on GitHub (pinned to d5fee525bf)

Solutions

  1. Verify the target dim size is 1 or equals the tensor's existing size at that dim before broadcasting
  2. Use the documented broadcast APIs (broadcast_as / broadcast_in) which handle unsqueezing, instead of low-level size construction
  3. Reshape the source tensor so the requested dim aligns with an existing size

Example fix

// before
// t: [3], requesting dim size 4 -> bails
let b = t.broadcast_as((4, 3))?; // adjust so requested sizes are compatible
// after
let b = t.broadcast_as((3, 3))?; // or reshape t first
Defensive patterns

Strategy: validation

Validate before calling

fn broadcast_ok(from: &[usize], to: &[usize]) -> bool {
    from.iter().rev().zip(to.iter().rev()).all(|(f, t)| *f == 1 || f == t)
}
assert!(broadcast_ok(&t.dims(), &target_shape));

Try / catch

let out = t.broadcast_as(target_shape)
    .or_else(|_| {
        // align shapes manually before broadcasting
        t.reshape(aligned_shape)?.broadcast_as(target_shape)
    })?;

Prevention

When it happens

Trigger: Calling broadcast/unsqueeze-style ops requesting dim size > allowed max, e.g. t.broadcast_as a shape with dim size 4 where the tensor has size 1 or rank 0 with size requested > 1.

Common situations: Broadcasting tensors whose shapes aren't broadcast-compatible (e.g. (3,) to (4,)); expanding a scalar to size >1 via the wrong API; config-driven target shapes inconsistent with the input.

Related errors


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