huggingface/candle · error

cannot use slice_set when self and src share their storage

Error message

cannot use slice_set when self and src share their storage

What it means

scatter_set mutates self's storage in place, so if self and source share the same underlying storage (e.g. one is a view of the other), in-place writing would corrupt the source data. Candle guards this aliasing case with an explicit bail. Note the message says 'slice_set' — a copy-paste from the sibling op — but it is thrown by Tensor::scatter_set.

Source

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

            .copy_strided_src(&mut storage, 0, self.layout())?;
        let layout = Layout::contiguous(shape);
        storage.scatter_set(
            &layout,
            &indexes.storage(),
            indexes.layout(),
            &source.storage(),
            source.layout(),
            dim,
        )?;
        let op = BackpropOp::new3(self, indexes, source, |t1, t2, t3| {
            Op::Scatter(t1, t2, t3, dim)
        });
        Ok(from_storage(storage, self.shape(), op, false))
    }

    pub fn scatter_set<D: Dim>(&self, indexes: &Self, source: &Self, dim: D) -> Result<()> {
        if self.same_storage(source) {
            crate::bail!("cannot use slice_set when self and src share their storage")
        }
        let dim = dim.to_index(self.shape(), "scatter-set")?;
        self.scatter_checks(indexes, source, dim)?;
        self.storage_mut().scatter_set(
            self.layout(),
            &indexes.storage(),
            indexes.layout(),
            &source.storage(),
            source.layout(),
            dim,
        )?;
        Ok(())
    }

    pub fn scatter_add<D: Dim>(&self, indexes: &Self, source: &Self, dim: D) -> Result<Self> {
        let dim = dim.to_index(self.shape(), "scatter-add")?;
        self.scatter_checks(indexes, source, dim)?;
        let shape = self.shape();

View on GitHub (pinned to d5fee525bf)

Solutions

  1. Make an independent copy of the source first: let src = src.copy()? (or to_device/to_dtype which materialize) so storages differ.
  2. Use the non-in-place scatter API instead of scatter_set, then assign the result.
  3. Restructure code so the destination and source are distinct buffers.

Example fix

// before
let src = t.i(0..2)?; // shares storage with t
t.scatter_set(&idx, &src, 0)?; // error
// after
let src = t.i(0..2)?.copy()?;
t.scatter_set(&idx, &src, 0)?;
Defensive patterns

Strategy: validation

Validate before calling

if t.same_storage(&src) {
    let src = src.copy()?;
}
t.scatter_set(&idx, &src, dim)?;

Try / catch

match t.scatter_set(&idx, &src, dim) {
    Ok(()) => (),
    Err(e) if e.to_string().contains("share their storage") => {
        let src = src.copy()?;
        t.scatter_set(&idx, &src, dim)?;
    }
    Err(e) => return Err(e.into()),
}

Prevention

When it happens

Trigger: Calling t.scatter_set(&indexes, &src, dim) where src was derived from t via views/reshapes/slices sharing storage (e.g. src = t.clone() at view level or src = t.i(..)).

Common situations: Chained in-place updates on tensors that alias each other; writing updated values back into a tensor using a slice of itself as source.

Related errors


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