huggingface/candle · error

ScatterND expects k (indices.shape[-1]) to be at most the…

Error message

ScatterND expects k (indices.shape[-1]) to be at most the rank of data

What it means

In ScatterND, the last dimension k of the `indices` tensor determines the depth of indexing (number of data dimensions being sliced). ONNX requires k <= rank(data). candle-onnx checks this upfront because indices deeper than the data rank are meaningless and cannot be processed.

Solutions

  1. Correct the indices tensor so its last dim equals (or is below) the data rank
  2. Fix upstream reshape/expand ops producing the wrong indices shape
  3. Re-export the model and compare ScatterND node inputs with netron

Example fix

# before: data rank 2, indices shape [4, 3]
# after
indices = indices[..., :2]  # slice trailing dim to match data rank
Defensive patterns

Strategy: validation

Validate before calling

let k = indices.dims()[indices.rank() - 1];
if k > data.rank() {
    return Err(format!("ScatterND k={} > data rank {}", k, data.rank()));
}

Type guard

fn scatternd_indices_valid(indices: &Tensor, data: &Tensor) -> bool {
    indices.rank() > 0 && indices.dims()[indices.rank() - 1] <= data.rank()
}

Try / catch

match eval(...) {
    Err(e) if e.contains("ScatterND expects k") => eprintln!("indices depth exceeds data rank"),
    other => other,
}

Prevention

When it happens

Trigger: Evaluating a ScatterND node where indices.shape[-1] exceeds data.rank(), e.g. 3-D indices into a 2-D tensor, often caused by mismatched indices/data produced upstream in the graph.

Common situations: Models where indices were built for a different data rank than actually flows into ScatterND; exporter bugs; hand-edited graphs after removing a dimension.

Related errors


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

Appendix: source

Thrown at candle-onnx/src/eval.rs:2441

            "ScatterND" => {
                let data = get(&node.input[0])?;

                let indices = get(&node.input[1])?;
                let indices = indices.to_dtype(DType::I64)?;

                let updates = get(&node.input[2])?;

                let reduction = get_attr_opt::<str>(node, "reduction")?.unwrap_or("none");

                let indices_shape = indices.dims();
                let data_shape = data.dims();
                let _updates_shape = updates.dims();

                // Last dimension of indices represents the depth of indexing
                let k = indices_shape.last().unwrap().clone();

                if k > data.rank() {
                    bail!("ScatterND expects k (indices.shape[-1]) to be at most the rank of data");
                }

                let num_updates = indices_shape[..indices_shape.len() - 1]
                    .iter()
                    .product::<usize>();

                let flat_indices = if indices.rank() == 1 && k == 1 {
                    indices.unsqueeze(0)?
                } else {
                    indices.reshape((num_updates, k))?
                };

                // Calculate the shape of each update element
                let update_element_shape = if k < data_shape.len() {
                    data_shape[k..].to_vec()
                } else {
                    vec![]
                };

View on GitHub (pinned to d5fee525bf)