GraphiteEditor/Graphite · error

Failed to get canvas context

Error message

Failed to get canvas context

What it means

fill_path_pattern creates a fresh 4x4 OffscreenCanvas per call and does get_context("2d").ok().flatten().expect("Failed to get canvas context"). get_context returns Err if the call throws and None if a 2D context cannot be created (context limits, memory pressure, or the runtime not fully implementing OffscreenCanvas 2D). This expect panics in WASM whenever context creation fails, which is most likely on browsers with partial OffscreenCanvas support or under resource exhaustion because a new canvas is allocated on every fill-tool hover.

Source

Thrown at editor/src/messages/portfolio/document/overlays/utility_types_web.rs:1026

	pub fn fill_path(&mut self, subpaths: impl Iterator<Item = impl Borrow<Subpath<PointId>>>, transform: DAffine2, color: &str) {
		self.push_path(subpaths, transform);

		self.render_context.set_fill_style_str(color);
		self.render_context.fill();
	}

	/// Fills the area inside the path with a pattern. Assumes `color` is an sRGB hex string.
	/// Used by the fill tool to show the area to be filled.
	pub fn fill_path_pattern(&mut self, subpaths: impl Iterator<Item = impl Borrow<Subpath<PointId>>>, transform: DAffine2, color: &str) {
		const PATTERN_WIDTH: usize = 4;
		const PATTERN_HEIGHT: usize = 4;

		let pattern_canvas = OffscreenCanvas::new(PATTERN_WIDTH as u32, PATTERN_HEIGHT as u32).unwrap();
		let pattern_context: OffscreenCanvasRenderingContext2d = pattern_canvas
			.get_context("2d")
			.ok()
			.flatten()
			.expect("Failed to get canvas context")
			.dyn_into()
			.expect("Context should be a canvas 2d context");

		// 4x4 pixels, 4 components (RGBA) per pixel
		let mut data = [0_u8; 4 * PATTERN_WIDTH * PATTERN_HEIGHT];

		let rgba = hex_to_rgba_u8(color);

		// ┌▄▄┬──┬──┬──┐
		// ├▀▀┼──┼──┼──┤
		// ├──┼──┼▄▄┼──┤
		// ├──┼──┼▀▀┼──┤
		// └──┴──┴──┴──┘
		let pixels = [(0, 0), (2, 2)];
		for &(x, y) in &pixels {
			let index = (x + y * PATTERN_WIDTH) * 4;
			data[index..index + 4].copy_from_slice(&rgba);
		}

View on GitHub (pinned to c507b35645)

Solutions

  1. Cache one shared 4x4 pattern OffscreenCanvas (and only rewrite its pixels when the color changes) instead of allocating per call
  2. Feature-detect OffscreenCanvas 2D once at startup and fall back to a document.createElement('canvas') path when unavailable
  3. Handle the None case with a log and a solid-color fill instead of expect, so the fill preview degrades instead of aborting

Example fix

// before
let pattern_context: OffscreenCanvasRenderingContext2d = pattern_canvas
	.get_context("2d")
	.ok()
	.flatten()
	.expect("Failed to get canvas context")
	.dyn_into()
	.expect("Context should be a canvas 2d context");

// after
let Some(ctx) = pattern_canvas.get_context("2d").ok().flatten() else {
	log::error!("Failed to get 2D context for fill pattern");
	return;
};
let pattern_context: OffscreenCanvasRenderingContext2d = ctx.dyn_into().expect("Context should be a canvas 2d context");
Defensive patterns

Strategy: validation

Validate before calling

// feature-detect once, and cache the pattern canvas instead of allocating per call
use wasm_bindgen::JsValue;
if !js_sys::Reflect::get(&wasm_bindgen::global(), &"OffscreenCanvas".into()).map(|v| !v.is_undefined()).unwrap_or(false) {
	// fall back to solid fill; do not call fill_path_pattern
}

Try / catch

match pattern_canvas.get_context("2d") {
	Ok(Some(ctx)) => { /* use ctx */ }
	Ok(None) => log::error!("2D context unavailable for fill pattern"),
	Err(e) => log::error!("get_context threw: {e:?}"),
}

Prevention

When it happens

Trigger: Invoking the fill tool overlay (fill_path_pattern) on a browser where OffscreenCanvas exists but get_context("2d") returns null (Safari < 16.4-era WebKit, some WebViews), or after allocating so many short-lived OffscreenCanvases that the browser refuses another context.

Common situations: Older Safari/iOS WebViews with incomplete OffscreenCanvas; headless test environments; memory-constrained devices where per-call canvas creation exhausts the context budget.

Related errors


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