huggingface/candle · error

only 2d matrixes are supported {lhs:?} {rhs:?}

Error message

only 2d matrixes are supported {lhs:?} {rhs:?}

What it means

broadcast_shape_matmul computes output shapes for matmul with broadcasting. It requires both operands to have rank >= 2 because the last two dimensions are the matrix being multiplied; a 1-D or 0-D tensor has no matrix dimensions, so the op bails.

Source

Thrown at candle-core/src/shape.rs:234

                l_value
            } else {
                Err(Error::ShapeMismatchBinaryOp {
                    lhs: lhs.clone(),
                    rhs: rhs.clone(),
                    op,
                }
                .bt())?
            }
        }
        Ok(Shape::from(bcast_dims))
    }

    pub(crate) fn broadcast_shape_matmul(&self, rhs: &Self) -> Result<(Shape, Shape)> {
        let lhs = self;
        let lhs_dims = lhs.dims();
        let rhs_dims = rhs.dims();
        if lhs_dims.len() < 2 || rhs_dims.len() < 2 {
            crate::bail!("only 2d matrixes are supported {lhs:?} {rhs:?}")
        }
        let (m, lhs_k) = (lhs_dims[lhs_dims.len() - 2], lhs_dims[lhs_dims.len() - 1]);
        let (rhs_k, n) = (rhs_dims[rhs_dims.len() - 2], rhs_dims[rhs_dims.len() - 1]);
        if lhs_k != rhs_k {
            crate::bail!("different inner dimensions in broadcast matmul {lhs:?} {rhs:?}")
        }

        let lhs_b = Self::from(&lhs_dims[..lhs_dims.len() - 2]);
        let rhs_b = Self::from(&rhs_dims[..rhs_dims.len() - 2]);
        let bcast = lhs_b.broadcast_shape_binary_op(&rhs_b, "broadcast_matmul")?;
        let bcast_dims = bcast.dims();

        let bcast_lhs = [bcast_dims, &[m, lhs_k]].concat();
        let bcast_rhs = [bcast_dims, &[rhs_k, n]].concat();
        Ok((Shape::from(bcast_lhs), Shape::from(bcast_rhs)))
    }
}

View on GitHub (pinned to d5fee525bf)

Solutions

  1. Promote 1-D tensors: use unsqueeze(0) on a vector-as-row or unsqueeze(1) for column, then squeeze the result
  2. Use Tensor::dot for 1-D inner products instead of matmul
  3. Check tensor ranks with .dims().len() before the op

Example fix

// before
let y = w.matmul(&x)?; // x is rank 1
// after
let y = w.matmul(&x.unsqueeze(1)?)?.squeeze(1)?;
Defensive patterns

Strategy: validation

Validate before calling

if lhs.dims().len() < 2 || rhs.dims().len() < 2 {
    return Err(anyhow::anyhow!("matmul requires rank >= 2, got {:?} x {:?}", lhs.dims(), rhs.dims()));
}

Try / catch

let y = match lhs.matmul(&rhs) {
    Ok(y) => y,
    Err(e) if e.to_string().contains("only 2d matrixes") => {
        let (a, b) = promote_to_2d(&lhs, &rhs)?;
        a.matmul(&b)?
    }
    Err(e) => return Err(e.into()),
};

Prevention

When it happens

Trigger: Calling Tensor::matmul / broadcast_matmul where either the LHS or RHS tensor has fewer than 2 dimensions, e.g. matmul of a 1-D vector against a matrix without unsqueezing.

Common situations: Dot products written as a.matmul(&b) with a rank-1 tensor; squeezing a batch dimension away before matmul; passing scalars/vectors produced by sum/mean reductions directly into matmul.

Related errors


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