garrytan/gstack · error · Error
2d canvas context unavailable
Error message
2d canvas context unavailable
What it means
Thrown by __rasterize() when canvas.getContext('2d') returns null after a successful SVG image decode. The browser refuses to hand out a 2D context when hardware-accelerated rendering is unavailable, the canvas has already been allocated a different context type, or the headless environment lacks GPU support. With no context the function cannot draw or call toDataURL, so it fails rather than returning a blank image.
Source
Thrown at lib/diagram-render/src/entry.ts:129
window.__rasterize = async (svgText: string, targetWidthPx: number): Promise<string> => {
assertTargetWidth(targetWidthPx);
const blob = new Blob([svgText], { type: "image/svg+xml;charset=utf-8" });
const url = URL.createObjectURL(blob);
try {
const img = new Image();
await new Promise<void>((resolve, reject) => {
img.onload = () => resolve();
img.onerror = () => reject(new Error("SVG image decode failed (malformed SVG or foreignObject content)"));
img.src = url;
});
const naturalW = img.naturalWidth || 800;
const naturalH = img.naturalHeight || 600;
const scale = targetWidthPx / naturalW;
const canvas = document.createElement("canvas");
canvas.width = Math.round(naturalW * scale);
canvas.height = Math.round(naturalH * scale);
const ctx = canvas.getContext("2d");
if (!ctx) throw new Error("2d canvas context unavailable");
ctx.fillStyle = "#ffffff";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
// Throws on tainted canvas — callers fall back to __mountForScreenshot +
// `browse screenshot --selector "#raster-stage"`.
return canvas.toDataURL("image/png");
} finally {
URL.revokeObjectURL(url);
}
};
/**
* Fallback rasterization stage: mount the SVG in the DOM so the caller can
* take an element screenshot (no canvas, no taint rules). Returns a marker
* string; the artifact is the screenshot, not the return value.
*/
window.__mountForScreenshot = (svgText: string, targetWidthPx: number): string => {
document.getElementById("raster-stage")?.remove();View on GitHub (pinned to 94993f7401)
Solutions
- Launch the browse daemon with software rendering enabled (e.g. add --use-gl=angle --use-angle=swiftshader or --enable-unsafe-swiftshader).
- Remove an unconditional --disable-gpu flag, or pair it with a software rasterizer backend.
- Fall back to __mountForScreenshot + `browse screenshot --selector "#raster-stage"` which needs no canvas.
- Verify the headless browser build bundles 2D canvas support.
Defensive patterns
Strategy: fallback
Try / catch
try {
return await __rasterize(svgText, targetWidthPx);
} catch (e) {
if (e instanceof Error && e.message === '2d canvas context unavailable') {
// Fallback: mount the SVG and screenshot the element instead
__mountForScreenshot(svgText, targetWidthPx);
return browseScreenshot('#raster-stage');
}
throw e;
} Prevention
- Launch the browse daemon with software rasterization flags (swiftshader).
- In CI, ensure the headless browser bundles 2D canvas support.
- Implement a __mountForScreenshot fallback wherever __rasterize is used.
- Smoke-test canvas.getContext('2d') at page load and warn if null.
When it happens
Trigger: document.createElement('canvas').getContext('2d') returns null inside the headless browser tab. Typical in headless Chromium with GPU disabled (--disable-gpu) and no software rasterizer, in a sandboxed CI environment, or after a prior call assigned a webgl context to the same canvas element (not the case here since each call creates a fresh canvas).
Common situations: CI runner with no GPU and --disable-gpu without SwiftShader; a constrained container that blocks GPU process access; an extremely memory-constrained tab that cannot allocate the backing store; rare browser build without 2d canvas support.
Related errors
- targetWidthPx out of range: ${px}
- invalid mermaid render id: ${id}
- excalidraw scene has no elements array
AI-assisted analysis of garrytan/gstack@94993f7401 (2026-08-12).
Data as JSON: /api/errors/485b07da1230ff3e.
Report an issue: GitHub.