GraphiteEditor/Graphite · error
window should have a document
Error message
window should have a document
What it means
`render_image_data_to_canvases` blits rasterized images onto canvases registered in `window.imageCanvases`. It already handles a missing `window` gracefully, but then calls `window.document()` — a `Option<Document>` — with `.expect("window should have a document")`. In a real browser a window always has a document, so this panic indicates a stubbed `window` object (tests, SSR shims) or an exotic embedding where the global `window` exists without a `document` property.
Source
Thrown at frontend/wrapper/src/helpers.rs:135
pub(crate) fn async_wake_callback() -> Wake {
use std::sync::Arc;
Arc::new(|| {
wasm_bindgen_futures::spawn_local(async {
wrapper(|wrapper| wrapper.dispatch(FutureMessage::Wake));
});
})
}
/// Blits each rasterized image to a canvas registered on `window.imageCanvases`
pub(crate) fn render_image_data_to_canvases<'a>(image_data: impl IntoIterator<Item = &'a RasterizedImage>) {
let window = match window() {
Some(window) => window,
None => {
error!("Cannot render canvas: window object not found");
return;
}
};
let document = window.document().expect("window should have a document");
let window_obj = Object::from(window);
let image_canvases_key = JsValue::from_str("imageCanvases");
let canvases_obj = match Reflect::get(&window_obj, &image_canvases_key) {
Ok(obj) if !obj.is_undefined() && !obj.is_null() => obj,
_ => {
let new_obj = Object::new();
if Reflect::set(&window_obj, &image_canvases_key, &new_obj).is_err() {
error!("Failed to create and set imageCanvases object on window");
return;
}
new_obj.into()
}
};
let canvases_obj = Object::from(canvases_obj);
for image in image_data {
let (placeholder_id, width, height) = (image.id, image.width, image.height);View on GitHub (pinned to c507b35645)
Solutions
- Treat it like the adjacent window check: `let Some(document) = window.document() else { error!(...); return; }`.
- In tests, use a complete DOM (jsdom with default url, or a real browser) instead of hand-written `window` doubles.
- Avoid loading/initializing the editor before the DOM exists (mount-time dynamic import).
- Verify in the host page that `window.document` is present before creating the wrapper.
Example fix
// before
let document = window.document().expect("window should have a document");
// after
let Some(document) = window.document() else {
error!("window has no document; cannot blit image canvases");
return;
}; Defensive patterns
Strategy: validation
Validate before calling
// TS: probe the DOM before enabling image-canvas previews
if (typeof window === 'undefined' || !window.document) {
console.warn('No window.document; image canvas blitting disabled');
} Type guard
function hasDocument(): boolean {
return typeof window !== 'undefined' && Boolean(window.document);
} Prevention
- Use a complete DOM in tests instead of hand-rolled window stubs.
- Mount the editor only after the document exists (post-DOMContentLoaded).
When it happens
Trigger: A `FrontendMessage::UpdateImageData` being emitted while the wrapper runs under a partial DOM mock whose `window` lacks `document` (jsdom configured without a document, custom test doubles, or a compromised/polyfilled global scope).
Common situations: Unit tests with hand-rolled `window` stubs; SSR harnesses that define `window` to guard imports but never attach a document; scripts that delete or replace `window.document`.
Related errors
- Failed to get canvas context
- Failed to get canvas context
- No global `window` exists
- Failed to call `requestAnimationFrame`
- Failed to call `setTimeout`
AI-assisted analysis of GraphiteEditor/Graphite@c507b35645 (2026-08-16).
Data as JSON: /api/errors/50a6bea92cf26d82.
Report an issue: GitHub.