GraphiteEditor/Graphite · error

Failed to create canvas element

Error message

Failed to create canvas element

What it means

While blitting each rasterized image, the helper calls `document.create_element("canvas")`, which returns `Result<Element, JsValue>` because the DOM method can throw (e.g. `InvalidCharacterError`). The `.expect("Failed to create canvas element")` panics on that. Since "canvas" is always a valid tag name in an HTML document, an `Err` here means the document isn't a normal HTML document (XML/SVG document, polyfilled DOM) or the environment is a partial mock.

Source

Thrown at frontend/wrapper/src/helpers.rs:164

			}
			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);
		let pixels = image.pixels.as_slice();
		let canvas_name = placeholder_id.to_string();
		let js_key = JsValue::from_str(&canvas_name);

		if Reflect::has(&canvases_obj, &js_key).unwrap_or(false) || width == 0 || height == 0 {
			continue;
		}

		let canvas: HtmlCanvasElement = document
			.create_element("canvas")
			.expect("Failed to create canvas element")
			.dyn_into::<HtmlCanvasElement>()
			.expect("Failed to cast element to HtmlCanvasElement");

		canvas.set_width(width);
		canvas.set_height(height);

		let context: CanvasRenderingContext2d = canvas
			.get_context("2d")
			.expect("Failed to get 2d context")
			.expect("2d context was not found")
			.dyn_into::<CanvasRenderingContext2d>()
			.expect("Failed to cast context to CanvasRenderingContext2d");
		let clamped_pixels = wasm_bindgen::Clamped(pixels);
		match ImageData::new_with_u8_clamped_array_and_sh(clamped_pixels, width, height) {
			Ok(image_data_obj) => {
				if context.put_image_data(&image_data_obj, 0, 0).is_err() {
					error!("Failed to put image data on canvas for id: {placeholder_id}");
				}

View on GitHub (pinned to c507b35645)

Solutions

  1. Replace the `.expect` with `match`/`let-else` that logs the image id and `continue`s to the next image, matching the graceful style of the surrounding code.
  2. In tests, run against a real browser DOM instead of partial mocks.
  3. Ensure the host page is a standard HTML document (no XHTML/XML document mode) where the wrapper is mounted.
  4. Probe once at startup: `document.createElement('canvas')` succeeds before enabling image-blitting paths.

Example fix

// before
let canvas: HtmlCanvasElement = document
  .create_element("canvas")
  .expect("Failed to create canvas element")
  .dyn_into::<HtmlCanvasElement>()
  .expect("Failed to cast element to HtmlCanvasElement");

// after
let canvas = match document.create_element("canvas") {
  Ok(element) => element,
  Err(err) => {
    error!("create_element('canvas') threw {err:?} for image id {placeholder_id}");
    continue;
  }
};
let Ok(canvas) = canvas.dyn_into::<HtmlCanvasElement>() else {
  error!("created element is not an HTMLCanvasElement for image id {placeholder_id}");
  continue;
};
Defensive patterns

Strategy: validation

Validate before calling

// TS: startup probe that createElement works before relying on image blitting
export const domCanCreateCanvas: boolean = (() => {
  try {
    return document.createElement('canvas') instanceof HTMLCanvasElement;
  } catch {
    return false;
  }
})();

Try / catch

JS cannot catch the wasm panic; gate blitting on the startup probe above and check `await editor.hasCrashed()` if the editor stops responding.

Prevention

When it happens

Trigger: `render_image_data_to_canvases` running against a document created by `document.implementation.createDocument(null, ...)` (XML mode), a DOM emulation where `createElement` throws or is missing, or a page whose `document` global was replaced.

Common situations: Embedding the editor's canvas blitting in test environments (jsdom variants, happy-dom) or non-HTML host documents; SSR shims with incomplete DOM APIs; rare custom-element registries that throw during creation.

Related errors


AI-assisted analysis of GraphiteEditor/Graphite@c507b35645 (2026-08-16). Data as JSON: /api/errors/d1211156c821e18c. Report an issue: GitHub.