{"record":{"id":"30df85c207c4c785","repo":"tracel-ai/burn","slug":"ndarray-scatter-nd-requires-contiguous-indices","errorCode":null,"errorMessage":"ndarray scatter_nd requires contiguous indices","messagePattern":"ndarray scatter_nd requires contiguous indices","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/burn-ndarray/src/ops/base.rs","lineNumber":325,"sourceCode":"        let data_shape: Vec<usize> = data.shape().to_vec();\n        let idx_shape: Vec<usize> = indices.shape().to_vec();\n        let m = idx_shape.len();\n        let k = idx_shape[m - 1];\n\n        // 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 {","sourceCodeStart":307,"sourceCodeEnd":343,"githubUrl":"https://github.com/tracel-ai/burn/blob/d16f7ba2ed0d41408189384044cc886fb4c8f957/crates/burn-ndarray/src/ops/base.rs#L307-L343","documentation":"In the same scatter_nd kernel, the indices tensor is flattened via as_slice, which returns None for non-contiguous storage; the expect panics with 'ndarray scatter_nd requires contiguous indices'. Like the data check, this reflects the backend kernel's contiguous-memory-only implementation.","triggerScenarios":"Calling scatter_nd where the indices tensor is a non-contiguous view (e.g. produced by slicing/indexing another tensor) on the ndarray backend.","commonSituations":"Building index tensors by slicing a larger index buffer; graph-captured models passing strided index intermediates; reusing views after permute operations.","solutions":["Copy the indices into a new owned contiguous tensor before calling scatter_nd.","Construct index tensors directly (from vec/IntNdArrayElement) instead of slicing existing ones.","Report as a bug if produced through burn's public high-level API, which should maintain contiguity."],"exampleFix":"// before\nlet idx = big_index_tensor.slice(s![.., 0]);\nlet out = data.scatter(idx, values);\n// after\nlet idx_owned = idx.to_data().convert::<IntNdArrayElement>();\nlet idx = NdArrayTensor::new(idx_owned.into_ndarray()); // contiguous copy\nlet out = data.scatter(idx, values);","handlingStrategy":"validation","validationCode":"// Copy indices into a fresh contiguous tensor before scatter\nlet idx_owned = indices.to_data().convert::<IntNdArrayElement>();\nlet indices = NdArrayTensor::new(idx_owned.into_ndarray());","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Construct index tensors directly from vectors instead of slicing","Make indices contiguous after any permute/slice","Cover scatter_nd with integration tests on the ndarray backend"],"tags":["rust","ndarray","tensor","indices","panic"],"backgroundTag":"non-contiguous-tensor","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"}