WebGPU adapter: pipeline creation forces entryPoint "main" when the descriptor omits it

Author: mesheltonCreated Sep 4, 2026Updated Sep 4, 2026

Environment: Electrobun 2.0.1 (Hutch 0.25.0), Cottontail main process, macOS 26.5.1 arm64, bundleWGPU: true, system webview.

What happens

GPUShaderModuleDescriptor.entryPoint is optional in WebGPU. When it is omitted, the spec requires the implementation to use the module's single entry point for that stage. The adapter instead substitutes the literal string "main", so any module whose entry point is named something else fails validation:

WGPU uncaptured error type=2: Entry point ""main"" doesn't exist in the shader module [ShaderModule (unlabeled)].
 - While validating vertex stage ([ShaderModule (unlabeled)], entryPoint: "main").
 - While validating vertex state.
 - While calling [Device].CreateRenderPipeline([RenderPipelineDescriptor]).

All three stages are affected. In api/sdks/main/webgpuAdapter.ts (2.0.1 devkit):

  • line 1555 — const vertexEntry = makeStringView(descriptor.vertex.entryPoint ?? "main");
  • line 1607 — const fragEntry = makeStringView(descriptor.fragment.entryPoint ?? "main");
  • line 1709 — const entry = makeStringView(descriptor.compute.entryPoint ?? "main");

Reproduction

No third-party library needed — the entry points here are deliberately not called main:

typescript
const module = device.createShaderModule({
  code: `
    @vertex fn vs() -> @builtin(position) vec4f { return vec4f(0, 0, 0, 1); }
    @fragment fn fs() -> @location(0) vec4f { return vec4f(1, 0, 0, 1); }
  `,
});

// entryPoint omitted on both stages, which is legal: one entry point per stage.
device.createRenderPipeline({
  layout: "auto",
  vertex: { module },
  fragment: { module, targets: [{ format: "bgra8unorm" }] },
});
// -> Entry point ""main"" doesn't exist in the shader module

Passing entryPoint: "vs" / entryPoint: "fs" explicitly works, which confirms the rest of the path is fine.

Why it matters

Libraries that generate WGSL generally do not name entry points main and do not set entryPoint, since the spec makes it optional. typegpu is one: it emits @vertex fn vs(...), so every pipeline it builds fails, render and compute alike. This is likely a contributing factor in other third-party WebGPU reports here.

Suggested fix

Pass the "undefined" form of WGPUStringViewdata = NULL with length = WGPU_STRLEN — rather than a literal, and let Dawn infer:

typescript
function makeUndefinedStringView() {
  return { ptr: 0, len: WGPU_STRLEN, cstr: null };
}

const vertexEntry = descriptor.vertex.entryPoint
  ? makeStringView(descriptor.vertex.entryPoint)
  : makeUndefinedStringView();

Note makeStringView(undefined) is not sufficient on its own: it returns { ptr: 0, len: 0n }, which is an empty string, semantically different from undefined, and does not trigger inference.

I verified this fix against 2.0.1 by patching all three call sites in a local devkit: the errors disappear, and a typegpu render pipeline plus a compute pipeline (perlin3d.staticCache) then build and render correctly at ~57fps.


Related, from the same session of getting typegpu running on the adapter: #542 (WebGPU global enum objects are not installed) and #543 (GPUBuffer.mapState is missing). The three are independent, and all three had to be worked around before a typegpu shader would render.