tracel-ai/burn · error
ndarray scatter_nd requires contiguous data
Error message
ndarray scatter_nd requires contiguous data
What it means
The ndarray backend's scatter_nd obtains a mutable contiguous slice of the output tensor via as_slice_mut and panics when the data is not stored contiguously (e.g. it is a strided view). The naive scatter implementation only supports contiguous memory, an internal invariant of this backend path.
Source
Thrown at crates/burn-ndarray/src/ops/base.rs:320
where
E: core::ops::Mul<Output = E> + PartialOrd,
{
use burn_backend::tensor::IndexingUpdateOp;
let data_shape: Vec<usize> = data.shape().to_vec();
let idx_shape: Vec<usize> = indices.shape().to_vec();
let m = idx_shape.len();
let k = idx_shape[m - 1];
// Number of index tuples = product of batch dims (first M-1 dims of indices)
let num_indices: usize = idx_shape[..m - 1].iter().product();
// Size of each slice to scatter = product of data.shape[K..]
let slice_size: usize = data_shape[k..].iter().product();
let mut output = data.into_owned();
let output_flat = output
.as_slice_mut()
.expect("ndarray scatter_nd requires contiguous data");
// Flatten indices to [num_indices, K]
let idx_flat = indices
.as_slice()
.expect("ndarray scatter_nd requires contiguous indices");
// Flatten values to [num_indices, slice_size]
let val_flat = values
.as_slice()
.expect("ndarray scatter_nd requires contiguous values");
let strides: Vec<usize> = {
let mut s = vec![0usize; k];
if k > 0 {
s[k - 1] = slice_size;
for i in (0..k - 1).rev() {
s[i] = s[i + 1] * data_shape[i + 1];
}View on GitHub (pinned to d16f7ba2ed)
Solutions
- Make the data tensor contiguous before scattering (clone/copy into a fresh owned ArrayD).
- Rearrange upstream ops to avoid strided views reaching scatter_nd.
- If this fires on standard high-level API usage, report it as a backend bug — upstream burn ops should never hand non-contiguous data to this kernel.
Example fix
// before let out = tensor.slice(s![.., ..2]).scatter(...); // after let contiguous = tensor.to_data().convert::<FloatNdArrayElement>(); let owned = NdArrayTensor::new(contiguous.into_ndarray()); // fresh contiguous buffer let out = owned.scatter(indices, values);
Defensive patterns
Strategy: validation
Validate before calling
// Ensure data owns a fresh contiguous buffer before scatter_nd let data_owned = data.to_data(); // deep copy, always contiguous let data = NdArrayTensor::new(data_owned.into_ndarray());
Prevention
- Clone/copy tensors after slicing or permuting before scatter
- Build scatter inputs from owned tensors, not views
- Test scatter paths with tensors produced by slice ops
When it happens
Trigger: Calling scatter_nd with a data tensor that is a non-contiguous view/slice produced by prior strided operations (permutes, slicing) on the ndarray backend.
Common situations: Chaining reshape/permute/slice operations before scatter_nd; exporting models (e.g. from ONNX-style graphs) that feed non-contiguous intermediates into scatter; custom backend code reusing views.
Related errors
- ndarray scatter_nd requires contiguous indices
- Requires autodiff tensor.
- an enabled float tensor must use an autodiff primitive
- Should be float, got int
- Should be float, got bool
AI-assisted analysis of tracel-ai/burn@d16f7ba2ed (2026-09-05).
Data as JSON: /api/errors/4d33fe3ce8939c42.
Report an issue: GitHub.