{"record":{"id":"e91686f332bb97b9","repo":"GraphiteEditor/Graphite","slug":"failed-to-cast-context-to-canvasrenderingcontext2d","errorCode":null,"errorMessage":"Failed to cast context to CanvasRenderingContext2d","messagePattern":"Failed to cast context to CanvasRenderingContext2d","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"frontend/wrapper/src/helpers.rs","lineNumber":176,"sourceCode":"\t\tif Reflect::has(&canvases_obj, &js_key).unwrap_or(false) || width == 0 || height == 0 {\n\t\t\tcontinue;\n\t\t}\n\n\t\tlet canvas: HtmlCanvasElement = document\n\t\t\t.create_element(\"canvas\")\n\t\t\t.expect(\"Failed to create canvas element\")\n\t\t\t.dyn_into::<HtmlCanvasElement>()\n\t\t\t.expect(\"Failed to cast element to HtmlCanvasElement\");\n\n\t\tcanvas.set_width(width);\n\t\tcanvas.set_height(height);\n\n\t\tlet context: CanvasRenderingContext2d = canvas\n\t\t\t.get_context(\"2d\")\n\t\t\t.expect(\"Failed to get 2d context\")\n\t\t\t.expect(\"2d context was not found\")\n\t\t\t.dyn_into::<CanvasRenderingContext2d>()\n\t\t\t.expect(\"Failed to cast context to CanvasRenderingContext2d\");\n\t\tlet clamped_pixels = wasm_bindgen::Clamped(pixels);\n\t\tmatch ImageData::new_with_u8_clamped_array_and_sh(clamped_pixels, width, height) {\n\t\t\tOk(image_data_obj) => {\n\t\t\t\tif context.put_image_data(&image_data_obj, 0, 0).is_err() {\n\t\t\t\t\terror!(\"Failed to put image data on canvas for id: {placeholder_id}\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tErr(e) => {\n\t\t\t\terror!(\"Failed to create ImageData for id: {placeholder_id}: {e:?}\");\n\t\t\t}\n\t\t}\n\n\t\tlet js_value = JsValue::from(canvas);\n\n\t\tif Reflect::set(&canvases_obj, &js_key, &js_value).is_err() {\n\t\t\terror!(\"Failed to set canvas '{canvas_name}' on imageCanvases object\");\n\t\t}\n\t}","sourceCodeStart":158,"sourceCodeEnd":194,"githubUrl":"https://github.com/GraphiteEditor/Graphite/blob/c507b356453361e31638b8bff8f6d46b6da2961e/frontend/wrapper/src/helpers.rs#L158-L194","documentation":"This panic fires in render_image_data_to_canvases in the wasm wrapper: after canvas.get_context(\"2d\") returns a value, dyn_into::<CanvasRenderingContext2d>() requires that JS object to be an instance of the standard 2D context class. The lookup succeeded but the returned object has the wrong type, so wasm-bindgen refuses the cast. Typical causes are a canvas whose context was already created in another mode (webgl/webgpu), an offscreen 2D context, or a polyfilled canvas.","triggerScenarios":"Calling get_context(\"2d\") on a canvas that wgpu or WebGL already acquired a context on; a canvas moved to offscreen via transferControlToOffscreen so getContext returns an OffscreenCanvasRenderingContext2D; running under jsdom or canvas polyfills that return non-standard objects.","commonSituations":"Blitting rasterized node-graph output onto a canvas that is also used as a wgpu render target; wasm unit tests under Node/jsdom; browsers or extensions overriding HTMLCanvasElement.prototype.getContext.","solutions":["Give 2D image-data blitting its own canvas element; never reuse a canvas that wgpu/WebGL has taken a context on","Narrow before casting: use dyn_ref::<CanvasRenderingContext2d>() and log an error instead of panicking when the type does not match","Avoid transferControlToOffscreen on canvases used by this code path, or bind to OffscreenCanvasRenderingContext2D instead","In test environments, run against real browser canvases (for example Playwright) rather than jsdom stubs"],"exampleFix":"// before\nlet context: CanvasRenderingContext2d = canvas\n\t.get_context(\"2d\")\n\t.expect(\"Failed to get 2d context\")\n\t.expect(\"2d context was not found\")\n\t.dyn_into::<CanvasRenderingContext2d>()\n\t.expect(\"Failed to cast context to CanvasRenderingContext2d\");\n\n// after\nlet Some(ctx) = canvas.get_context(\"2d\").ok().flatten() else {\n\terror!(\"2d context unavailable for placeholder {placeholder_id}\");\n\treturn;\n};\nlet Ok(context) = ctx.dyn_into::<CanvasRenderingContext2d>() else {\n\terror!(\"context for placeholder {placeholder_id} is not CanvasRenderingContext2d\");\n\treturn;\n};","handlingStrategy":"type-guard","validationCode":"// before drawing, confirm the canvas can still yield a usable 2D context value\ncanvas.get_context(\"2d\").ok().flatten().is_some();","typeGuard":"fn as_2d_context(value: &JsValue) -> Option<CanvasRenderingContext2d> {\n\tvalue.dyn_ref::<CanvasRenderingContext2d>().cloned()\n}","tryCatchPattern":"match ctx_value.dyn_into::<CanvasRenderingContext2d>() {\n\tOk(context) => blit(context, image_data),\n\tErr(value) => error!(\"unexpected context type: {:?}\", value.js_typeof()),\n}","preventionTips":["Keep one canvas per backend: 2D blitting and wgpu/WebGL surfaces on separate elements","Treat dyn_into on DOM objects as fallible; prefer dyn_ref plus logging in all new code","Never transferControlToOffscreen a canvas that later code calls get_context(\"2d\") on"],"tags":["wasm-bindgen","canvas","dyn-into","frontend"],"backgroundTag":"canvas-context-type-mismatch","analyzedSha":"c507b356453361e31638b8bff8f6d46b6da2961e","analyzedAt":"2026-08-16T21:57:18.596Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}