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
- Feature-detect WebGPU (navigator.gpu / successful adapter request) before the wgpu path and fall back to CPU rendering
- Use a dedicated canvas for the wgpu surface and never call get_context("2d") on it
- 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
- Request a compatible adapter before building the executor in canvas environments
- Keep wgpu-surface canvases and 2D canvases strictly separate
- Retain a JS reference to surface canvases so they cannot be garbage-collected mid-frame
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
- Failed to create surface
- GPU executor should be available when we receive a texture
- Failed to create WGPU context
- Failed to create WgpuExecutor
- Failed to create canvas element
AI-assisted analysis of GraphiteEditor/Graphite@c507b35645 (2026-08-16).
Data as JSON: /api/errors/c3f7e333ddd8aba3.
Report an issue: GitHub.