{"record":{"id":"823cd3c4d928539f","repo":"tracel-ai/burn","slug":"ndarray-scatter-nd-requires-contiguous-values","errorCode":null,"errorMessage":"ndarray scatter_nd requires contiguous values","messagePattern":"ndarray scatter_nd requires contiguous values","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/burn-ndarray/src/ops/base.rs","lineNumber":330,"sourceCode":"        // Number of index tuples = product of batch dims (first M-1 dims of indices)\n        let num_indices: usize = idx_shape[..m - 1].iter().product();\n        // Size of each slice to scatter = product of data.shape[K..]\n        let slice_size: usize = data_shape[k..].iter().product();\n\n        let mut output = data.into_owned();\n        let output_flat = output\n            .as_slice_mut()\n            .expect(\"ndarray scatter_nd requires contiguous data\");\n\n        // Flatten indices to [num_indices, K]\n        let idx_flat = indices\n            .as_slice()\n            .expect(\"ndarray scatter_nd requires contiguous indices\");\n\n        // Flatten values to [num_indices, slice_size]\n        let val_flat = values\n            .as_slice()\n            .expect(\"ndarray scatter_nd requires contiguous values\");\n\n        let strides: Vec<usize> = {\n            let mut s = vec![0usize; k];\n            if k > 0 {\n                s[k - 1] = slice_size;\n                for i in (0..k - 1).rev() {\n                    s[i] = s[i + 1] * data_shape[i + 1];\n                }\n            }\n            s\n        };\n\n        for n in 0..num_indices {\n            // Compute flat base offset from the K-dimensional index\n            let mut base_offset = 0usize;\n            for j in 0..k {\n                let idx_val = idx_flat[n * k + j].elem::<i64>() as usize;\n                base_offset += idx_val * strides[j];","sourceCodeStart":312,"sourceCodeEnd":348,"githubUrl":"https://github.com/tracel-ai/burn/blob/d16f7ba2ed0d41408189384044cc886fb4c8f957/crates/burn-ndarray/src/ops/base.rs#L312-L348","documentation":"scatter_nd writes slices of `values` into `data` at positions given by `indices`. The ndarray backend needs both arrays as flat contiguous buffers, so it calls `as_slice()`, which returns None for non-contiguous arrays (e.g. views with strides, broadcast arrays, or slices of a larger array) and this expect() panics. This is a backend implementation constraint, not a user-facing error message.","triggerScenarios":"Calling Tensor::scatter (scatter_nd) on the ndarray backend with a `values` tensor that is a non-contiguous view - typically a slice, permuted/transposed view, or broadcasted tensor - so that ArrayD::as_slice() yields None. Also happens if the tensor was produced by an op returning a zero-stride view that was never copied into owned memory.","commonSituations":"Scattering into a tensor obtained from slice/select operations without calling .into_owned() or .contiguous() first; combining scatter with transpose/permute in a data-prep pipeline; passing a broadcast-expanded tensor as values.","solutions":["Call .contiguous() (or float_into_contiguous / .into_owned()) on the values tensor before scatter, or restructure so values comes from a contiguous op (e.g. cat, from_data)","Check for intervening slice/permute/broadcast ops on values and insert an explicit copy","If hitting this from library-internal code, report/fix the op to call into_owned() before as_slice(), as gather ops do"],"exampleFix":"// before\nlet values = tensor_slice.transpose();\nlet out = data.scatter(indices, values);\n// after\nlet values = tensor_slice.transpose().contiguous();\nlet out = data.scatter(indices, values);","handlingStrategy":"validation","validationCode":"fn ensure_contiguous<E: burn_ndarray::FloatElement>(t: &Tensor<NdArray<E>, D>) -> Tensor<NdArray<E>, D> {\n    // force materialization of any zero-stride/strided view\n    t.clone().float_into_contiguous() // or t.clone().contiguous() depending on dtype\n}\n// call before: let out = data.scatter(indices, ensure_contiguous(&values));","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Call .contiguous() on tensors produced by slice/transpose/broadcast before scatter","Avoid passing broadcast-expanded tensors as scatter values","Keep scatter operands as results of contiguous ops (from_data, cat, matmul)","In tests, assert tensor.to_data() round-trips without panic on your pipeline shapes"],"tags":["rust","ndarray","panic","scatter","contiguity"],"backgroundTag":"non-contiguous-tensor-view","analyzedSha":"d16f7ba2ed0d41408189384044cc886fb4c8f957","analyzedAt":"2026-09-05T13:19:14.260Z","contentChangedAt":"2026-09-05T13:19:14.260Z","schemaVersion":2},"datasetVersion":"2026-09-12T17:17:11.597Z"}