Troubleshooting
ModelLoadError: Could not fetch model from ...
The .onnx couldn’t be downloaded. Causes, in rough order of likelihood:
- Offline / blocked CDN. Default model URL is jsdelivr. If your env
blocks it, self-host and pass
modelUrl. - Wrong
modelUrl. Double-check the path; the error message includes the URL it tried. - CORS. Cross-origin model fetches need
Access-Control-Allow-Originon the response. jsdelivr sets it; self-hosting on a subdomain may not. - HTTP status non-200. The error includes the status:
(HTTP 404).
import { ModelLoadError } from 'quadscan';
try { await Quadscan.scan(file); }
catch (err) {
if (err instanceof ModelLoadError) {
// err.message includes URL + status
// err.cause is the underlying fetch error (TypeError, AbortError, ...)
}
}
ORT wasmPaths 404s
Symptom: console errors like GET .../ort-wasm-simd-threaded.wasm 404.
onnxruntime-web defaults to walking up from its own module URL to find
the .wasm runtimes, which fails under most modern bundlers. Quadscan
overrides this to point at jsdelivr by default. If you set wasmPaths
yourself, make sure:
- The path ends with a trailing slash:
/ort/not/ort. - The files exist at that URL:
ort-wasm.wasm,ort-wasm-simd-threaded.wasm, plus the matching.mjsloader scripts. - They’re served with
Content-Type: application/wasm.
WebGPU unavailable, silent fallback to WASM
This is by design. Default executionProviders is ['webgpu', 'wasm'];
ORT silently falls back. Detect it explicitly with:
const q = new Quadscan({ executionProviders: ['webgpu'] });
try { await q.initialize(); }
catch { /* WebGPU not supported, retry with ['wasm'] */ }
WebGPU is unavailable in:
- Firefox < 141 by default,
- Safari < 18.2 by default,
- any browser without an unlocked GPU (some CI / headless setups).
”No document detected”
ScanResult came back { success: false, message: 'No document detected...' }.
The model ran but the peak confidence on the heatmap was below
minDetectionConfidence (default 0.1). Causes:
- Tightly cropped image. The model expects ~10% padding around the document. Photograph wider, or pre-pad before scanning.
- Document fills the entire frame. Corners are off-image; the decoder can’t peak. Same fix.
- Very low contrast. Try the FastViT preset:
new Quadscan({ model: 'fastvit' })(slower, more accurate).
You can also lower the threshold:
await Quadscan.scan(file, { minDetectionConfidence: 0.05 });
DegenerateQuadError
The detected (or supplied) corners form a non-convex / collinear quad. Usually means the document is rotated > 45°. The reorder step can’t tell which corner is which.
Fixes:
- Pre-rotate the input to the nearest 90°.
- Implement a Hungarian-assignment wrapper around
scan. - Or skip detection and provide corners manually via
extract().
AbortError
Standard DOM behavior: pass an AbortSignal in ScanOptions and call
controller.abort(). scan() throws DOMException('AbortError') at the
next checkpoint.
const ctrl = new AbortController();
button.onclick = () => ctrl.abort();
try {
await q.scan(file, { signal: ctrl.signal });
} catch (err) {
if (err instanceof DOMException && err.name === 'AbortError') return;
throw err;
}
Note: ORT inference itself is not interruptible mid-call. Abort takes effect between steps (preprocess → inference → decode → warp).