Learn · 8
Debugging
WebGPU fails quietly. It does not throw on a bad pipeline — it discards the command buffer and hands you numbers that are merely wrong. This lesson is the set of questions you can ask the device, and every one of them exists because something here went wrong silently first.
Is this a real GPU?
A headless browser will hand you a software rasterizer, and it is still an adapter,
so nothing throws. Timings measured there belong to the rasterizer, not to this
library. probe() answers before anything is initialised and separates
"the browser has no WebGPU" from "the driver refused".
Did the GPU reject anything?
Validation errors arrive on an event, not as exceptions. The device counts them and keeps the first message, because a broken shader repeats its error on every subsequent dispatch and scrolls the real cause off the screen. A benchmark once reported milliseconds per step while holding an invalid command buffer — that number was not a measurement, it was the wall clock of a model that was not learning.
What is it holding?
Two different questions, so two numbers. memory asks "is this leaking"
and deliberately excludes the pool; pooled asks "how large is the
footprint" and counts what is waiting to be reused. The pool grows when shapes
change — buffers are kept per size, so batch 16's cannot serve batch 32.
Did a gradient actually flow?
The trap that value-comparing tests cannot see: an operation returns the right
numbers and quietly drops the gradient. Both roll and
maskedSelect were cut that way on the GPU path while the golden table
was entirely green. Asking is cheap — run backward and look at whether
.grad arrived at all.
Errors say what torch says
When borch refuses, it refuses with the message torch uses, so the phrase you would paste into a search engine is the same one. That equivalence is tested: 12 error cases and 9 searchable messages.
What the library says first
Five things a first page does that make it silently slow or wrong, and for each the
library says one sentence on the console — once, before the number is wrong: an
inference loop with no scope (at the two-thousandth buffer kept), an
eval() model run with gradients on, a large matmul off the multiples of
eight the fast kernel wants, a laptop on battery, and a first call that took long
(its kernels compiling). Device.advised holds what was said;
Device.advice = false silences a bench that knows what it is doing.
What to remember
- When numbers are wrong but nothing threw, ask the device: check shapes, dtypes, and for NaN/Inf.
- Confirm gradients actually flow — a zero or missing
.gradis a broken graph. - Read the intermediate values (print or
showthem); do not guess. - A flat or exploding loss is a signal, not a mystery (see Fix the bug).
- Read the console once: the library says the five common traps first, each once.
docs/BOOK.md) under "Seven places where green can be a lie". The device counters
above exist because three of them were found the hard way.