Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

[Web] [WebGPU] GatherND `computeSliceOffsets` emits `i32(vec4<u32>)` for data rank >= 5 — Tint rejects the ShaderModule

Open Beginner friendly
#32,731 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 2 days

@qjia7 is already working on this.

Since Sep 22, 2026.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
74/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
javascript

Research direction

Start at the JSEP WebGPU GatherND computeSliceOffsets program builder and inspect its generated input_dims and sizes_from_slice_dims_data indexing. Compare the dynamic packed-array indexing with Slice's calculateInputIndices helper, then verify that rank-5 GatherND shaders compile and produce values matching the CPU EP.

Written by the indexing model from the issue text.

Description

ep:WebGPU platform:web
Describe the issue

Running any model containing a GatherND node whose data input has rank >= 5 on the WebGPU execution provider fails at shader-compilation time. The generated computeSliceOffsets helper kernel contains:

index += i32(uniforms.input_dims[input_dim_idx]);

input_dim_idx is a dynamic index (uniforms.batch_dims + dim_idx), and for rank >= 5 the input_dims uniform array is vec4-packed, so the expression yields vec4<u32>. Tint rejects it:

:28:18 error: no matching constructor for 'i32(vec4<u32>)'
[Invalid ShaderModule "computeSliceOffsets"] is invalid.

followed by the usual Invalid ComputePipeline / BindGroup / CommandBuffer cascade. The same graph runs fine on the WASM/CPU EP, and the equivalent rank-4 GatherND compiles and runs fine on WebGPU.

To reproduce

Minimal model (opset 17): GatherND, data float16 [1,1,4,4,8], indices int64 [1,1,8,2] (values in [0,4)), batch_dims = 2 → output float16 [1,1,8,8].

import numpy as np, onnx
from onnx import helper, TensorProto
data = helper.make_tensor_value_info('data', TensorProto.FLOAT16, [1,1,4,4,8])
idx = helper.make_tensor_value_info('indices', TensorProto.INT64, [1,1,8,2])
out = helper.make_tensor_value_info('out', TensorProto.FLOAT16, [1,1,8,8])
node = helper.make_node('GatherND', ['data','indices'], ['out'], batch_dims=2)
g = helper.make_graph([node], 'h1', [data, idx], [out], [])
m = helper.make_model(g, opset_imports=[helper.make_opsetid('', 17)])
m.ir_version = 8
onnx.save(m, 'h1_gathernd_r5.onnx')
const sess = await ort.InferenceSession.create(bytes, { executionProviders: ['webgpu'] });
await sess.run({ data, indices }); // WGSL validation errors, no usable output

Control: the same node with rank-4 data ([1,4,4,8], indices [1,8,2], batch_dims=1) works on WebGPU and is bit-identical to WASM.

Root cause (from the generated template)

In the JSEP WebGPU GatherND implementation (computeSliceOffsets program builder), the uniforms are declared as

{ name: 'input_dims', type: 'u32', length: o.length } // o = data dims

and the shader source is generated as

${o.length === 1
  ? 'index += i32(uniforms.input_dims);'
  : 'index += i32(uniforms.input_dims[input_dim_idx]);'}

input_dim_idx is dynamic, so once the array is vec4-packed (length > 4, i.e. data rank >= 5) the subscript returns a whole vec4<u32> and i32(...) has no matching constructor. The sibling expression uniforms.sizes_from_slice_dims_data[dim_idx] has the same latent hazard for slice-dim counts > 4 (our graphs have 2, so only the first one fires). Other ops (e.g. Slice's calculateInputIndices) index packed uniform arrays through a helper that decomposes the dynamic index — GatherND's template does raw indexing instead. The one-line fix should be the same decomposition (vec-index + component swizzle) or keeping input_dims unpacked.

Expected behavior

Rank >= 5 GatherND compiles and returns the same values as the CPU EP (verified: the gather itself is well-defined; an exact Reshape+Gather rewrite of the node produces bit-identical results on CPU).

Environment

  • onnxruntime-web: 1.29.0, 1.30.0, and 1.31.0-dev.20260918-bc8e7ed75 (all three emit the identical broken shader)
  • Browser: Chrome (current), real GPU (AMD RDNA3, maxStorageBuffersPerShaderStage: 10)
  • OS: Windows 11

Real-world impact

The BiRefNet Lite 512 matting graph contains 80 such GatherNDs (all rank-5 fp16 data, batch_dims=2, in the deformable-attention path), so the model is unusable on WebGPU without graph surgery. Happy to provide the full-graph repro or test a fix.

Urgency

No response

ONNX Runtime Installation

Built from Source

ONNX Runtime Version or Commit ID

1.29.0, 1.30.0, and 1.31.0-dev.20260918-bc8e7ed75

Execution Provider

'webgpu' (WebGPU)

Dominant language
C++
Stars
22.2k
Forks
4.4k
Avg merge
3d 5h
Merged PRs (30d)
312

Getting set up

Open in Codespaces

Starts the project's dev container in your browser, under your own GitHub account.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from microsoft/onnxruntime

All issues in microsoft/onnxruntime

Similar issues

More C++ issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.