linebender/druid · critical
unable to create surface
Error message
unable to create surface
What it means
Surface::new asks the compositor to allocate a new WlSurface; if the compositor returns None the library panics with this message. This indicates the Wayland compositor refused or failed to create the underlying protocol surface, so no window can be constructed.
Solutions
- Verify the Wayland environment is healthy: echo $WAYLAND_DISPLAY / $XDG_RUNTIME_DIR and confirm the socket exists before creating windows.
- Test with a known-good compositor (e.g. weston, sway, GNOME Wayland) to rule out compositor-specific limitations.
- Check that create_surface returning None is not a stub/test-double behavior in your environment.
- Reconnect to the compositor (rebuild the handle) and retry surface creation if the compositor restarted.
Defensive patterns
Strategy: validation
Validate before calling
// Validate the Wayland environment before Surface::new
fn wayland_env_ok() -> bool {
std::env::var("WAYLAND_DISPLAY").is_ok()
&& std::env::var("XDG_RUNTIME_DIR")
.map(|d| std::path::Path::new(&d).exists())
.unwrap_or(false)
} Try / catch
let surface = std::panic::catch_unwind(AssertUnwindSafe(|| {
Surface::new(&compositor, handler, size)
}))
.map_err(|_| anyhow::anyhow!("compositor failed to create surface"))?; Prevention
- Check WAYLAND_DISPLAY and XDG_RUNTIME_DIR before launching
- Fall back to X11 backend when the Wayland compositor is unavailable
- Test against a real compositor, not stubs, before shipping
- Log compositor connection health at startup
When it happens
Trigger: Calling Surface::new (public constructor) with a compositor whose create_surface() returns None — i.e. the compositor cannot allocate a new wl_surface at that moment.
Common situations: Connecting to a broken or half-initialized Wayland compositor, WAYLAND_DISPLAY / XDG_RUNTIME_DIR pointing at a stale socket, compositor resource exhaustion, or a custom/test compositor stub that always returns None from create_surface.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- unable to acquire underlying compositor to create an xdg…
- unable to acquire underlying compositor to create an xdg…
- unexpected wayland event
- unrecognised key event
- attaching an already in-use surface
AI-assisted analysis of linebender/druid@0f8b1195e4 (2026-09-10).
Data as JSON: /api/errors/67e7bfe87cb9886a.
Report an issue: GitHub.
Appendix: source
Thrown at druid-shell/src/backend/wayland/surfaces/surface.rs:50
pub struct Surface {
pub(super) inner: std::sync::Arc<Data>,
}
impl From<std::sync::Arc<Data>> for Surface {
fn from(d: std::sync::Arc<Data>) -> Self {
Self { inner: d }
}
}
impl Surface {
pub fn new(
c: impl Into<CompositorHandle>,
handler: Box<dyn window::WinHandler>,
initial_size: kurbo::Size,
) -> Self {
let compositor = CompositorHandle::new(c);
let wl_surface = match compositor.create_surface() {
None => panic!("unable to create surface"),
Some(v) => v,
};
let current = std::sync::Arc::new(Data {
compositor: compositor.clone(),
wl_surface: RefCell::new(wl_surface),
outputs: RefCell::new(std::collections::HashSet::new()),
buffers: buffers::Buffers::new(compositor.shared_mem(), initial_size.into()),
logical_size: Cell::new(initial_size),
scale: Cell::new(1),
anim_frame_requested: Cell::new(false),
handler: RefCell::new(handler),
idle_queue: std::sync::Arc::new(std::sync::Mutex::new(vec![])),
active_text_input: Cell::new(None),
damaged_region: RefCell::new(Region::EMPTY),
deferred_tasks: RefCell::new(std::collections::VecDeque::new()),
});
View on GitHub (pinned to 0f8b1195e4)