GraphiteEditor/Graphite · error

Failed to create surface from canvas

Error message

Failed to create surface from canvas

What it means

CanvasSurfaceHandle::surface lazily asks the wgpu instance to create a surface targeting the stored HTML canvas and panics when that fails. Surface creation fails when a GPU context cannot attach to the element: an existing incompatible context, a browser without WebGPU, or an instance that cannot present to canvases.

Source

Thrown at node-graph/libraries/canvas-utils/src/wasm.rs:68

		self.get().set_resolution(resolution);
	}
}

#[cfg(feature = "wgpu")]
pub struct CanvasSurfaceHandle(CanvasHandle, Option<Arc<WgpuSurface>>);
#[cfg(feature = "wgpu")]
impl CanvasSurfaceHandle {
	pub fn new() -> Self {
		Self(CanvasHandle::new(), None)
	}
	fn surface(&mut self, executor: &WgpuExecutor) -> &WgpuSurface {
		if self.1.is_none() {
			let canvas = self.0.get().canvas.clone();
			let surface = executor
				.context()
				.instance
				.create_surface(wgpu::SurfaceTarget::Canvas(canvas))
				.expect("Failed to create surface from canvas");
			self.1 = Some(Arc::new(surface));
		}
		self.1.as_ref().unwrap()
	}
}
#[cfg(feature = "wgpu")]
impl Canvas for CanvasSurfaceHandle {
	fn id(&mut self) -> CanvasId {
		self.0.id()
	}
	fn context(&mut self) -> CanvasRenderingContext2d {
		self.0.context()
	}
	fn set_resolution(&mut self, resolution: glam::UVec2) {
		self.0.set_resolution(resolution);
	}
}
#[cfg(feature = "wgpu")]

View on GitHub (pinned to c507b35645)

Solutions

  1. Feature-detect WebGPU (navigator.gpu / successful adapter request) before the wgpu path and fall back to CPU rendering
  2. Use a dedicated canvas for the wgpu surface and never call get_context("2d") on it
  3. Handle the Result from create_surface and log wgpu's error so the graph can degrade instead of panicking

Example fix

// before
let surface = executor.context().instance.create_surface(wgpu::SurfaceTarget::Canvas(canvas)).expect("Failed to create surface from canvas");

// after
let surface = executor.context().instance.create_surface(wgpu::SurfaceTarget::Canvas(canvas)).unwrap_or_else(|e| {
	panic!("Failed to create surface from canvas: {e}")
});
Defensive patterns

Strategy: validation

Validate before calling

// host page: only enable the wgpu path when WebGPU is present
// if (!navigator.gpu) { /* stay on the CPU render path */ }

Type guard

fn webgpu_available() -> bool {
	js_sys::Reflect::get(&js_sys::global(), &JsValue::from_str("gpu"))
		.map(|v| !v.is_undefined())
		.unwrap_or(false)
}

Try / catch

match instance.create_surface(wgpu::SurfaceTarget::Canvas(canvas)) {
	Ok(surface) => surface,
	Err(e) => return Err(format!("wgpu surface creation failed: {e}").into()),
}

Prevention

When it happens

Trigger: The canvas already having a 2D context acquired elsewhere (context-type conflict); running in a browser without navigator.gpu; the canvas element being detached or garbage-collected by the time the surface is created.

Common situations: Safari or Firefox without WebGPU enabled; graphs with GPU nodes where the canvas is shared with 2D blitting; headless test browsers without GPU acceleration.

Related errors


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