niri-wm/niri · critical

no X11 support

Error message

no X11 support

What it means

Unmapped wraps a newly created window between creation and its initial xdg configure. toplevel() unwraps Window::toplevel(), which returns Some only for Wayland xdg_shell toplevels and None for X11 (Xwayland) windows — hence the message 'no X11 support'. X11 windows are not supposed to enter the unmapped map at all, so this expect firing means an X11 window reached a Wayland-only code path: an internal invariant violation (bug), and it panics the compositor.

Source

Thrown at src/window/unmapped.rs:98

impl Unmapped {
    /// Wraps a newly created window that hasn't been initially configured yet.
    pub fn new(window: Window) -> Self {
        Self {
            window,
            state: InitialConfigureState::NotConfigured {
                wants_fullscreen: None,
                wants_maximized: false,
            },
            activation_token_data: None,
        }
    }

    pub fn needs_initial_configure(&self) -> bool {
        matches!(self.state, InitialConfigureState::NotConfigured { .. })
    }

    pub fn toplevel(&self) -> &ToplevelSurface {
        self.window.toplevel().expect("no X11 support")
    }
}

View on GitHub (pinned to 606284464d)

Solutions

  1. Update niri — invariant panics on X11 windows are bug fixes (check the issue tracker for 'no X11 support' backtraces).
  2. Capture the backtrace from the crash log (journalctl --user -u niri or stderr) and report it with the exact X11 app that triggered it.
  3. As a workaround, avoid the triggering app or run X11 apps through a translation layer (xwayland-satellite) instead of direct Xwayland mapping.
  4. If developing against niri sources, ensure X11 windows bypass the unmapped/initial-configure path (only xdg toplevels should be wrapped in Unmapped::new).

Example fix

// before: panics if an X11 window ever gets wrapped
pub fn toplevel(&self) -> &ToplevelSurface {
    self.window.toplevel().expect("no X11 support")
}

// after: keep the invariant at construction instead — only wrap xdg toplevels
if let Some(_toplevel) = window.toplevel() {
    unmapped.insert(Unmapped::new(window.clone()));
}
Defensive patterns

Strategy: type-guard

Validate before calling

// Rust: only wrap windows that can receive an xdg initial configure
if window.toplevel().is_some() {
    unmapped.insert(Unmapped::new(window.clone()));
} else {
    debug!("skipping unmapped tracking for non-xdg window (X11)");
}

Type guard

fn as_xdg_toplevel(window: &smithay::desktop::Window) -> Option<&ToplevelSurface> {
    window.toplevel() // Some only for Wayland xdg_shell toplevels, None for X11 windows
}

Try / catch

let toplevel = self
    .window
    .toplevel()
    .ok_or_else(|| anyhow!("window has no xdg toplevel (X11 windows never reach the unmapped state)"))?;
// returning Err instead of expect keeps the compositor alive and the bug reportable

Prevention

When it happens

Trigger: An Xwayland client's window being inserted into the Unmapped flow (the map keyed for new windows awaiting initial configure) — e.g. a regression in how X11 windows are routed during map/unmap, typically triggered by starting or closing an X11 app under Xwayland.

Common situations: Hitting a niri bug after an update when an X app (or one run via xwayland-satellite style paths) maps a window; race conditions around Xwayland startup at session start; building niri from a dev branch with incomplete X11 handling.

Related errors


AI-assisted analysis of niri-wm/niri@606284464d (2026-08-16). Data as JSON: /api/errors/262ca5110ae75234. Report an issue: GitHub.