niri-wm/niri · error · anyhow::Error

failed to register any org.freedesktop.ScreenSaver interface

Error message

failed to register any org.freedesktop.ScreenSaver interface

What it means

niri's idle-inhibit D-Bus server exports its ScreenSaver implementation at both /org/freedesktop/ScreenSaver and /ScreenSaver on the session bus; zbus's object_server().at() returns Ok(false) when an interface with that name is already registered at the path. If BOTH paths are taken, niri bails with this message — some other process in the session is already serving org.freedesktop.ScreenSaver, so registering would silently shadow it.

Source

Thrown at src/dbus/freedesktop_screensaver.rs:149

impl Start for ScreenSaver {
    fn start(self) -> anyhow::Result<zbus::blocking::Connection> {
        let is_inhibited = self.is_inhibited.clone();
        let is_broken = self.is_broken.clone();
        let inhibitors = self.inhibitors.clone();
        let monitor_task = self.monitor_task.clone();

        let conn = zbus::blocking::Connection::session()?;
        let flags = RequestNameFlags::AllowReplacement
            | RequestNameFlags::ReplaceExisting
            | RequestNameFlags::DoNotQueue;

        let org_fd_ss_registered = conn
            .object_server()
            .at("/org/freedesktop/ScreenSaver", self.clone())?;
        let ss_registered = conn.object_server().at("/ScreenSaver", self)?;

        if !org_fd_ss_registered && !ss_registered {
            anyhow::bail!("failed to register any org.freedesktop.ScreenSaver interface")
        }

        conn.request_name_with_flags("org.freedesktop.ScreenSaver", flags)?;

        let async_conn = conn.inner();
        let future = {
            let conn = async_conn.clone();
            async move {
                if let Err(err) =
                    monitor_disappeared_clients(&conn, is_inhibited.clone(), inhibitors.clone())
                        .await
                {
                    warn!("error monitoring org.freedesktop.ScreenSaver clients: {err:?}");
                    is_broken.store(true, Ordering::SeqCst);
                    is_inhibited.store(false, Ordering::SeqCst);
                    inhibitors.lock().unwrap().clear();
                }
            }

View on GitHub (pinned to 606284464d)

Solutions

  1. Find the current owner: 'busctl --user tree | grep -i screensaver' or 'gdbus call --session --dest org.freedesktop.DBus --object-path /org/freedesktop/DBus --method org.freedesktop.DBus.ListNames', then stop that conflicting service.
  2. Kill leftover instances: 'pgrep -a niri' should show exactly one; 'pkill niri' and also check for stale Xwayland/portal processes.
  3. If nested inside another DE, reset the session bus context: 'systemctl --user stop gnome-session.target xdg-desktop-portal*' or run niri under its own bus (dbus-run-session niri).
  4. If two compositors must coexist, give the second one a private bus so the name/object paths do not collide.

Example fix

# before: conflicting owner on the session bus -> "failed to register any org.freedesktop.ScreenSaver interface"
niri &
# after: clean up the previous owner first
busctl --user tree | grep -i screensaver   # identify it
systemctl --user stop xdg-desktop-portal.service
pkill -x niri
niri
Defensive patterns

Strategy: try-catch

Validate before calling

// Rust (zbus): check for an existing owner before exporting your interfaces
let dbus = zbus::blocking::fdo::DBusProxy::new(&conn)?;
let has_owner = dbus.name_has_owner("org.freedesktop.ScreenSaver".into())?;
if has_owner {
    warn!("another ScreenSaver provider is active; skipping registration");
}

Try / catch

let a = conn.object_server().at("/org/freedesktop/ScreenSaver", iface.clone()).is_ok();
let b = conn.object_server().at("/ScreenSaver", iface).is_ok();
if !a && !b {
    // degraded mode: feature unavailable, not fatal — log and continue serving everything else
    warn!("failed to register any org.freedesktop.ScreenSaver interface");
}

Prevention

When it happens

Trigger: Running niri while another screen-saver provider owns those object paths: a leftover/nested niri instance, xdg-desktop-portal or a portal frontend, or a desktop session manager (e.g. GNOME components started via systemd user units) that also implements org.freedesktop.ScreenSaver on the same session bus.

Common situations: Starting niri from within an existing desktop session (GNOME/KDE services still running); a stale niri process after a crash-logout-login cycle; running two compositors side by side for testing on the same DBUS_SESSION_BUS_ADDRESS; systemd user services from a previous DE still active.


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