{"record":{"id":"5ea98dca69298944","repo":"mozilla/pdf.js","slug":"canvas-is-not-specified","errorCode":null,"errorMessage":"Canvas is not specified","messagePattern":"Canvas is not specified","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/display/canvas_factory.js","lineNumber":46,"sourceCode":"    this.#enableHWA = enableHWA;\n  }\n\n  create(width, height) {\n    if (width <= 0 || height <= 0) {\n      throw new Error(\"Invalid canvas size\");\n    }\n    const canvas = this._createCanvas(width, height);\n    return {\n      canvas,\n      context: canvas.getContext(\"2d\", {\n        willReadFrequently: !this.#enableHWA,\n      }),\n    };\n  }\n\n  reset({ canvas }, width, height) {\n    if (!canvas) {\n      throw new Error(\"Canvas is not specified\");\n    }\n    if (width <= 0 || height <= 0) {\n      throw new Error(\"Invalid canvas size\");\n    }\n    canvas.width = width;\n    canvas.height = height;\n  }\n\n  destroy(canvasAndContext) {\n    const { canvas } = canvasAndContext;\n    if (!canvas) {\n      throw new Error(\"Canvas is not specified\");\n    }\n    // Zeroing the width and height cause Firefox to release graphics\n    // resources immediately, which can greatly reduce memory consumption.\n    canvas.width = canvas.height = 0;\n    canvasAndContext.canvas = null;\n    canvasAndContext.context = null;","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/mozilla/pdf.js/blob/5903d58d58e4dd9ce6ffa3834aea8480f06b4ada/src/display/canvas_factory.js#L28-L64","documentation":"Thrown by BaseCanvasFactory.reset({ canvas }, width, height) when the canvas property of the first argument is falsy. reset() reuses an existing canvas/context pair to avoid allocations, so a null canvas is a programming error — the caller lost the reference, typically by destroying it earlier.","triggerScenarios":"Calling reset({ canvas: null }, ...) after destroy() nulled canvasAndContext.canvas; passing a fresh empty object; destructuring mismatch where the canvas field name is wrong.","commonSituations":"Reusing a canvasAndContext across render cycles after destroy() zeroed its fields; pooling logic that hands out the same slot twice; refactoring that renamed canvas to ctx.","solutions":["Do not call reset() after destroy(); allocate a new canvas with create() instead.","Track lifecycle so reset() is only called on live canvasAndContext objects.","Add a null-check before reset() and recreate if needed."],"exampleFix":"// before\ncanvasFactory.destroy(c);\ncanvasFactory.reset(c, w, h);\n\n// after\ncanvasFactory.destroy(c);\nc = canvasFactory.create(w, h);","handlingStrategy":"type-guard","validationCode":"if (canvasAndContext?.canvas) {\n  canvasFactory.reset(canvasAndContext, w, h);\n} else {\n  canvasAndContext = canvasFactory.create(w, h);\n}","typeGuard":"const isLiveCanvasPair = (v): v is { canvas: HTMLCanvasElement; context: CanvasRenderingContext2D } =>\n  !!v && !!v.canvas && !!v.context;","tryCatchPattern":"null","preventionTips":["Never call reset() after destroy() on the same object.","Null the reference after destroy().","Single-source lifecycle ownership for canvasAndContext."],"tags":["canvas","lifecycle","validation"],"backgroundTag":null,"analyzedSha":"5903d58d58e4dd9ce6ffa3834aea8480f06b4ada","analyzedAt":"2026-08-13T02:28:27.364Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}