Validation vs Async Errors

Synchronous Validation Versus Asynchronous Errors

The content timeline (your JavaScript thread) checks only what it can decide at once, such as a misspelled enum or a missing member, and those fail as exceptions. Anything needing the device's state (usages, limits, formats) is checked on the device timeline in the GPU process (Encoders and Queues): the call returns at once, possibly with an invalid object, and the error arrives later as a GPUError.

Where a WebGPU error surfaces: an exception now, or a GPUError later
Where a WebGPU error surfaces: an exception now, or a GPUError later
Two mistakes throw; two return normally and are reported laterHTMLLive
<script type="module">
  const device = await (await navigator.gpu.requestAdapter()).requestDevice();
  device.onuncapturederror = (e) => console.log(`later: ${e.error.message.split('\n')[0]}`);
  const attempt = (what, fn) => { try { fn(); console.log(`${what}: returned`); }
    catch (e) { console.log(`${what}: threw ${e.name}: ${e.message.split(': ').pop()}`); } };
  attempt('no size', () => device.createBuffer({ usage: GPUBufferUsage.COPY_DST }));
  attempt('mapped 6 B', () => device.createBuffer({ size: 6, usage: 8,
    mappedAtCreation: true }));
  attempt('usage 0', () => device.createBuffer({ size: 4, usage: 0 }));
  const buffer = device.createBuffer({ size: 4, usage: GPUBufferUsage.COPY_DST });
  attempt('16 B into 4', () => device.queue.writeBuffer(buffer, 0, new Float32Array(4)));
</script>

Waiting on the GPU process for each call would stall the page, so invalidity spreads instead: a bind group made from an invalid buffer is invalid, as is a command buffer using it.