Stirling-Tools/Stirling-PDF · error · Error
Canvas 2D context unavailable
Error message
Canvas 2D context unavailable
What it means
Thrown by `renderPdfPageToCanvas` when `HTMLCanvasElement.getContext("2d")` returns null. Browsers return null when they cannot allocate a 2D context — usually because the page is out of GPU/JS memory, an active context-limit has been hit, or the canvas dimensions are too large. Here pages are rendered at scale 2, so a large page yields an enormous backing store.
Source
Thrown at frontend/editor/src/core/hooks/tools/adjustContrast/useAdjustContrastOperation.ts:29
import { applyAdjustmentsToCanvas } from "@app/components/tools/adjustContrast/utils";
import { pdfWorkerManager } from "@app/services/pdfWorkerManager";
import { createFileFromApiResponse } from "@app/utils/fileResponseUtils";
import { getPdfiumModule, saveRawDocument } from "@app/services/pdfiumService";
import { copyRgbaToBgraHeap } from "@app/utils/pdfiumBitmapUtils";
import type { PDFDocumentProxy } from "pdfjs-dist";
async function renderPdfPageToCanvas(
pdf: PDFDocumentProxy,
pageNumber: number,
scale: number,
): Promise<HTMLCanvasElement> {
const page = await pdf.getPage(pageNumber);
const viewport = page.getViewport({ scale });
const canvas = document.createElement("canvas");
canvas.width = viewport.width;
canvas.height = viewport.height;
const ctx = canvas.getContext("2d");
if (!ctx) throw new Error("Canvas 2D context unavailable");
await page.render({ canvasContext: ctx, canvas, viewport }).promise;
return canvas;
}
// Render, adjust, and assemble all pages of a single PDF into a new PDF using PDFium
async function buildAdjustedPdfForFile(
file: File,
params: AdjustContrastParameters,
): Promise<File> {
const m = await getPdfiumModule();
const arrayBuffer = await file.arrayBuffer();
const pdf = await pdfWorkerManager.createDocument(arrayBuffer, {});
const pageCount = pdf.numPages;
const docPtr = m.FPDF_CreateNewDocument();
if (!docPtr) throw new Error("PDFium: failed to create document");
try {View on GitHub (pinned to 9ef20dcab8)
Solutions
- Reduce the render scale (2 → 1) for large pages, or compute scale from page dimensions to cap backing-store pixels.
- Free intermediate canvases explicitly (set width/height to 0 and drop references) after each page so the context pool is not exhausted.
- Cap page pixel dimensions and downscale before getContext, splitting oversized pages into tiles if needed.
- Detect navigator/Worker memory and warn the user before attempting very large PDFs.
Example fix
// before
const canvas = document.createElement("canvas");
canvas.width = viewport.width;
canvas.height = viewport.height;
const ctx = canvas.getContext("2d");
if (!ctx) throw new Error("Canvas 2D context unavailable");
// after — downscale to a max backing-store size and retry
const MAX_PIXELS = 4096 * 4096;
let scale = 2;
let viewport = page.getViewport({ scale });
while (viewport.width * viewport.height > MAX_PIXELS && scale > 0.5) {
scale -= 0.25;
viewport = page.getViewport({ scale });
}
const canvas = document.createElement("canvas");
canvas.width = viewport.width;
canvas.height = viewport.height;
const ctx = canvas.getContext("2d");
if (!ctx) throw new Error(`Canvas 2D context unavailable (page ${pageNumber} at scale ${scale})`); Defensive patterns
Strategy: validation
Validate before calling
// Cap backing-store size before requesting a 2D context
const MAX_PIXELS = 4096 * 4096;
let scale = 2;
let vp = page.getViewport({ scale });
while (vp.width * vp.height > MAX_PIXELS && scale > 0.5) {
scale -= 0.25;
vp = page.getViewport({ scale });
} Type guard
function has2dContext(c: CanvasRenderingContext2D | null): c is CanvasRenderingContext2D {
return c !== null;
} Try / catch
try {
return await renderPdfPageToCanvas(pdf, pageNumber, scale);
} catch (e) {
if (e instanceof Error && e.message === "Canvas 2D context unavailable") {
// retry once at a lower scale, then warn the user
return await renderPdfPageToCanvas(pdf, pageNumber, Math.max(scale / 2, 0.5));
}
throw e;
} Prevention
- Downscale oversized pages before rendering so the backing store stays within browser limits.
- Free intermediate canvases (zero out width/height) after each page to avoid hitting the context limit.
- Warn users before processing extremely large PDFs in the adjust-contrast tool.
When it happens
Trigger: A very large page (e.g. A0/poster or a high-DPI scan) rendered at scale 2 exceeds the max canvas backing-store size; too many canvases are alive simultaneously (per-tab context limit ~16 in some browsers); the tab is under heavy memory pressure after processing many files.
Common situations: Adjust-contrast on a multi-megapixel scanned PDF; running on a low-memory device or a tab that has been open for hours; browser hardware acceleration disabled forcing software allocation failure.
Related errors
- PDFium: failed to create document
- Could not get canvas context
- Could not get canvas context
- Failed to get canvas context
- Unable to acquire 2D canvas context.
AI-assisted analysis of Stirling-Tools/Stirling-PDF@9ef20dcab8 (2026-08-13).
Data as JSON: /api/errors/a8fca56a346f9c8c.
Report an issue: GitHub.