{"record":{"id":"760a3a522c46299f","repo":"tracel-ai/burn","slug":"ndarray-gather-nd-requires-contiguous-indices","errorCode":null,"errorMessage":"ndarray gather_nd requires contiguous indices","messagePattern":"ndarray gather_nd requires contiguous indices","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/burn-ndarray/src/ops/base.rs","lineNumber":416,"sourceCode":"        let k = idx_shape[m - 1];\n\n        // Number of index tuples\n        let num_indices: usize = idx_shape[..m - 1].iter().product();\n        // Size of each output slice\n        let slice_size: usize = data_shape[k..].iter().product();\n\n        // Output shape: idx_shape[..m-1] ++ data_shape[k..]\n        let mut out_shape_vec: Vec<usize> = idx_shape[..m - 1].to_vec();\n        out_shape_vec.extend_from_slice(&data_shape[k..]);\n        let out_total = num_indices * slice_size;\n\n        let data_flat = data\n            .as_slice()\n            .expect(\"ndarray gather_nd requires contiguous data\");\n\n        let idx_flat = indices\n            .as_slice()\n            .expect(\"ndarray gather_nd requires contiguous indices\");\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        let mut output_vec: Vec<E> = vec![0.elem::<E>(); out_total];\n\n        for n in 0..num_indices {\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;","sourceCodeStart":398,"sourceCodeEnd":434,"githubUrl":"https://github.com/tracel-ai/burn/blob/d16f7ba2ed0d41408189384044cc886fb4c8f957/crates/burn-ndarray/src/ops/base.rs#L398-L434","documentation":"gather_nd flattens `indices` with as_slice() for direct flat indexing; non-contiguous index arrays make as_slice() return None and this expect() panics. Indices must be a densely stored integer tensor.","triggerScenarios":"Calling Tensor::gather (gather_nd) where the `indices` tensor is a non-contiguous view (slice, permute, broadcast, or result of zero-stride expand), so ArrayD::as_slice() returns None.","commonSituations":"Building index tensors via broadcasting/expand then gathering; slicing a larger index tensor before gather; ONNX graphs where GatherND indices come from a transposed tensor.","solutions":["Call .contiguous() (or .into_owned()) on the indices tensor before gather","Construct index tensors contiguously from the start (from_data/from_ints) instead of reshaping views","If indices come from arithmetic on views, insert a materializing op (e.g. cat of one tensor) to force a copy"],"exampleFix":"// before\nlet idx = raw_idx.slice([0..n]).transpose();\nlet out = data.gather(idx);\n// after\nlet idx = raw_idx.slice([0..n]).transpose().contiguous();\nlet out = data.gather(idx);","handlingStrategy":"validation","validationCode":"fn contiguous_idx(t: &Tensor<NdArray<Ibd>, D>) -> Tensor<NdArray<Ibd>, D> {\n    t.clone().int_into_contiguous() // materialize index tensor before gather\n}\n// let out = data.gather(contiguous_idx(&indices));","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Build index tensors contiguously (from_data/from_ints) rather than reshaping views","Insert .contiguous() after any slice/permute of indices","Avoid broadcasting/expand to construct gather indices; build the full tensor instead"],"tags":["rust","ndarray","panic","gather","contiguity","indices"],"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"}