GraphiteEditor/Graphite · error

Failed to cast element to HtmlCanvasElement

Error message

Failed to cast element to HtmlCanvasElement

What it means

After `document.create_element("canvas")` succeeds, the wrapper converts the JS value to a typed `HtmlCanvasElement` with `dyn_into`, which performs a JS `instanceof`-style check. The `.expect("Failed to cast element to HtmlCanvasElement")` panics when the created object is not actually an `HTMLCanvasElement` — possible when the document is not an HTML document (XML/SVG documents produce generic elements) or a polyfilled DOM returns a mock that fails the brand check.

Source

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

		}
	};
	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}");
				}
			}
			Err(e) => {

View on GitHub (pinned to c507b35645)

Solutions

  1. Use `let Ok(canvas) = element.dyn_into::<HtmlCanvasElement>() else { ... continue; }` so one uncastable element skips one image instead of crashing.
  2. Ensure the wrapper runs in a standard HTML document in a real browser.
  3. In tests, prefer a real browser runner; if using jsdom, verify `document.createElement('canvas') instanceof HTMLCanvasElement` holds.
  4. Feature-detect before the loop: bail out early if a probe canvas fails the `instanceof` check.

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 element = document.create_element("canvas").unwrap_or_else(|err| {
  panic!("create_element('canvas') threw {err:?}")
});
let Ok(canvas) = element.dyn_into::<HtmlCanvasElement>() else {
  error!("element is not an HTMLCanvasElement; skipping image id {placeholder_id}");
  continue;
};
Defensive patterns

Strategy: type-guard

Validate before calling

// TS: verify the environment yields real canvas elements
export const realCanvasElements: boolean = (() => {
  try {
    return document.createElement('canvas') instanceof HTMLCanvasElement;
  } catch {
    return false;
  }
})();

Type guard

function isHtmlCanvasElement(el: unknown): el is HTMLCanvasElement {
  return typeof HTMLCanvasElement !== 'undefined' && el instanceof HTMLCanvasElement;
}

Prevention

When it happens

Trigger: Running `render_image_data_to_canvases` in an XML/SVG-mode document or DOM emulation where `createElement('canvas')` returns an object failing `instanceof HTMLCanvasElement`; web-sys and the actual host DOM disagreeing on element interfaces (heavy polyfills, patched globals).

Common situations: DOM-emulation test setups (jsdom variants returning generic Element mocks); editors embedded into SVG or XML host documents; environments where `HTMLCanvasElement` is polyfilled without proper branding.

Related errors


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