{"record":{"id":"fa3eecf6d473c158","repo":"GraphiteEditor/Graphite","slug":"should-have-a-window-in-this-context","errorCode":null,"errorMessage":"should have a window in this context","messagePattern":"should have a window in this context","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"node-graph/libraries/canvas-utils/src/wasm.rs","lineNumber":153,"sourceCode":"\t\t\t},\n\t\t\tsource_texture.size(),\n\t\t);\n\n\t\tsurface_texture.queue.submit([encoder.finish()]);\n\t\tsurface_texture.present();\n\t}\n}\n\n/// A wgpu surface backed by an HTML canvas element.\n/// Holds a reference to the canvas to prevent garbage collection.\npub struct CanvasImpl {\n\tcanvas_id: u64,\n\tcanvas: HtmlCanvasElement,\n}\n\nimpl CanvasImpl {\n\tfn new() -> Self {\n\t\tlet document = window().expect(\"should have a window in this context\").document().expect(\"window should have a document\");\n\n\t\tlet canvas: HtmlCanvasElement = document.create_element(\"canvas\").unwrap().dyn_into::<HtmlCanvasElement>().unwrap();\n\t\tlet canvas_id = CANVAS_IDS.fetch_add(1, Ordering::SeqCst);\n\n\t\t// Store the canvas in the global scope so it doesn't get garbage collected\n\t\tlet window = window().expect(\"should have a window in this context\");\n\t\tlet window_obj = Object::from(window);\n\n\t\tlet image_canvases_key = JsValue::from_str(CANVASES_OBJECT_KEY);\n\n\t\tlet mut canvases = Reflect::get(&window_obj, &image_canvases_key);\n\t\tif canvases.is_err() || canvases.as_ref().map_or(false, |v| v.is_undefined() || v.is_null()) {\n\t\t\tReflect::set(&window_obj.clone(), &image_canvases_key, &Object::new()).unwrap();\n\t\t\tcanvases = Reflect::get(&window_obj, &image_canvases_key);\n\t\t}\n\n\t\t// Convert key and value to JsValue\n\t\tlet js_key = JsValue::from_str(canvas_id.to_string().as_str());","sourceCodeStart":135,"sourceCodeEnd":171,"githubUrl":"https://github.com/GraphiteEditor/Graphite/blob/c507b356453361e31638b8bff8f6d46b6da2961e/node-graph/libraries/canvas-utils/src/wasm.rs#L135-L171","documentation":"CanvasImpl::new calls web_sys::window() to reach the document for creating a canvas element; window() returns None whenever the global object is not a Window — web workers expose self rather than window, and Node has neither. Creating a canvas in such an environment panics at this first lookup.","triggerScenarios":"Instantiating canvas-backed nodes inside a web worker; running the wasm module under Node without a DOM shim; server-side evaluation of the module during SSR passes.","commonSituations":"Moving node graph evaluation off the main thread; wasm tests executed in worker-based runtimes; Node scripts that load the editor binary for rendering.","solutions":["Run canvas-dependent node code on the browser main thread","In workers, target OffscreenCanvas and access globals through globalThis instead of window","For tests, run under a DOM environment (jsdom) or gate canvas creation behind a main-thread check"],"exampleFix":"// before\nlet document = window().expect(\"should have a window in this context\").document().expect(\"window should have a document\");\n\n// after\nlet Some(window) = window() else {\n\tpanic!(\"cannot create a canvas outside a browser window; run on the main thread or use OffscreenCanvas\")\n};\nlet document = window.document().expect(\"window should have a document\");","handlingStrategy":"validation","validationCode":"fn has_browser_window() -> bool {\n\tweb_sys::window().is_some()\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep canvas-backed node evaluation on the browser main thread","In workers, use OffscreenCanvas with globalThis instead of window","Run wasm tests under a DOM-providing runtime before touching canvas code"],"tags":["wasm","web-sys","window","worker"],"backgroundTag":"window-undefined","analyzedSha":"c507b356453361e31638b8bff8f6d46b6da2961e","analyzedAt":"2026-08-16T21:57:18.596Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}