gfx-rs/wgpu · error
expected valid handle for canvas
Error message
expected valid handle for canvas
What it means
create_surface on the web backend panics when the raw window/canvas handle passed to it is not one of the recognized web surface targets (an HTMLCanvasElement or OffscreenCanvas). The 'Params' hit in benches is unrelated code; the actual condition is that SurfaceTargetRaw or the converted handle did not match any supported web canvas variant.
Source
Thrown at wgpu/src/backend/webgpu.rs:1702
.expect("expected to find single canvas")
.into();
canvas_node.into()
}
raw_window_handle::RawWindowHandle::WebCanvas(handle) => {
let value: &JsValue = unsafe { handle.obj.cast().as_ref() };
value.clone().unchecked_into()
}
raw_window_handle::RawWindowHandle::WebOffscreenCanvas(handle) => {
let value: &JsValue = unsafe { handle.obj.cast().as_ref() };
let canvas: web_sys::OffscreenCanvas = value.clone().unchecked_into();
let context_result = canvas.get_context("webgpu");
return self.create_surface_from_context(
Canvas::Offscreen(canvas),
context_result,
);
}
_ => panic!("expected valid handle for canvas"),
};
let context_result = canvas_element.get_context("webgpu");
self.create_surface_from_context(Canvas::Canvas(canvas_element), context_result)
}
}
}
fn request_adapter(
&self,
options: &crate::RequestAdapterOptions<'_, '_>,
) -> Pin<Box<dyn dispatch::RequestAdapterFuture>> {
let requested_backends = self.requested_backends;
//TODO: support this check, return `None` if the flag is not set.
// It's not trivial, since we need the Future logic to have this check,
// and currently the Future here has no room for extra parameter `backends`.
if !(requested_backends.contains(wgt::Backends::BROWSER_WEBGPU)) {View on GitHub (pinned to 3e11ff59bf)
Solutions
- On web, create surfaces from an HTMLCanvasElement or OffscreenCanvas (or pass the canvas via raw handle tagged as web canvas) rather than native window handles.
- Use a raw-window-handle web variant (WebCanvas/WebDisplayHandle) that carries the DOM canvas id.
- Gate surface creation code per-platform so native handles never reach the wasm build.
Example fix
// before let surface = instance.create_surface(native_window).unwrap(); // wasm32: panics // after (web) let surface = instance.create_surface(wgpu::SurfaceTarget::Canvas(canvas_element)).unwrap();
Defensive patterns
Strategy: validation
Validate before calling
// only pass DOM canvases as surface targets on wasm assert!(cfg!(target_arch = "wasm32") == canvas_is_web_target, "native window handles cannot create surfaces on web");
Type guard
fn is_valid_web_surface_target(t: &wgpu::SurfaceTargetRaw) -> bool {
matches!(t, wgpu::SurfaceTargetRaw::WebCanvas(_) | wgpu::SurfaceTargetRaw::WebOffscreenCanvas(_))
} Prevention
- On web, always create surfaces from HTMLCanvasElement/OffscreenCanvas or raw-window-handle WebCanvas/WebDisplayHandle.
- Keep native handle-based surface creation behind cfg(not(target_arch = "wasm32")).
- Pin consistent wgpu versions so SurfaceTarget variants match across the workspace.
When it happens
Trigger: Calling instance.create_surface with a raw handle whose downcast to a web canvas type fails, e.g. passing a native window handle (Window, VkSurfaceKHR, etc.) to a wasm32-web build, or a corrupted/unexpected SurfaceTargetRaw enum value.
Common situations: Shared cross-platform code passing native window handles on web; using RawWindowHandle from a non-DOM source; mixing wgpu versions where SurfaceTarget variants changed.
Related errors
- multi-plane textures are not supported
- Clamp to border is not supported
- Accessing the GPU is only supported on the main thread or fr
- found `ShaderSource::Dummy`
- Web backend does not support arrays of buffers
AI-assisted analysis of gfx-rs/wgpu@3e11ff59bf (2026-09-03).
Data as JSON: /api/errors/eca9ada890548917.
Report an issue: GitHub.