Handle render failures
Classify a rejection, decide whether retrying can help, and keep the last good image when a GPU fault is transient.
Turn a rejected render into a decision: fix the input, retry, or fall back.
1. Normalise whatever was thrown
RenderError.from never throws, so it is safe as the first line of a catch.
import { RenderError, renderImage } from 'nanoraster';
try {
const image = await renderImage(glb, { format: 'webp' });
console.log(image.name);
} catch (error) {
const failure = RenderError.from(error);
console.error(failure.code, failure.message);
}2. Split transient faults from input faults
isGpuFault is true for the three codes that describe the device rather than
the request: the only ones worth retrying.
try {
await renderImage(glb, { format: 'webp' });
} catch (error) {
const failure = RenderError.from(error);
if (!failure.isGpuFault) {
throw failure; // driver-unsupported, parse, encode, unknown — retrying changes nothing
}
// adapter-unavailable, device-lost, gpu — worth another attempt
}3. Retry only the transient class
import { RenderError, renderImage, type RenderedImageFile } from 'nanoraster';
const renderWithRetry = async (glb: Uint8Array<ArrayBuffer>, attempts = 3): Promise<RenderedImageFile> => {
for (let attempt = 1; ; attempt += 1) {
try {
return await renderImage(glb, { format: 'webp' });
} catch (error) {
const failure = RenderError.from(error);
if (!failure.isGpuFault || attempt >= attempts) throw failure;
}
}
};Retrying a parse failure loops forever, so the guard checks the class before
the attempt count.
4. Keep the last good image
A transient GPU fault should not blank the last good image.
let lastGood: RenderedImageFile | undefined;
try {
lastGood = await renderImage(glb, { format: 'webp' });
} catch (error) {
const failure = RenderError.from(error);
if (!failure.isGpuFault) throw failure;
// lastGood still holds the previous render
}5. Tell a missing GPU from a slow one
adapter-unavailable means no adapter at all. A host can also succeed with a
software adapter — SwiftShader, lavapipe, WARP — that renders correctly but
an order of magnitude slower. describeAdapter is the preflight that
separates the two before you commit to a workload. It resolves undefined
when there is no adapter, rather than rejecting:
import { describeAdapter } from 'nanoraster';
const adapter = await describeAdapter();
if (!adapter) {
// Nothing will render here: fall back now.
} else if (adapter.deviceType === 'cpu') {
// Renders work; budget ~10x the usual latency or reduce sizes.
}backend and name round out the answer for a bug report. In browsers
deviceType is 'unknown' unless the adapter declares itself a fallback:
WebGPU withholds the class, and a browser with only software support usually
returns no adapter at all.
Variations
Distinguishing bad GLB from bad options. Both surface as parse, since
both violate the input contract; message names which.
A disposed renderer. Calls on a disposed Renderer
reject with code gpu and the message gpu: renderer disposed. Unlike other
GPU-class faults this one is permanent: recreate the renderer, do not retry
the call.
JPEG with a transparent background. Fails as encode, not parse: the
request is well-formed but the encoder cannot represent it; see
Format and annotate.
Unknown failures. unknown means the thrown value carried no recognised
tag; do not retry. Every code and its retry guidance is listed under
RenderError.