Hmbown/CodeWhale · error · ExecError

display enumeration needs xrandr (X11) or swaymsg/hyprctl…

Error message

display enumeration needs xrandr (X11) or swaymsg/hyprctl (Wayland) — install one and retry

What it means

Thrown by the list_displays capability when no supported display-enumeration tool is available for the detected session: xrandr on X11, or swaymsg/hyprctl on Wayland. The backend probes session and tool availability first; if none of the expected binaries are present it fails with this explicit install hint rather than returning an empty list.

Solutions

  1. On X11 install xrandr (x11-xserver-utils on Debian/Ubuntu) and ensure DISPLAY is set.
  2. On sway install swaymsg (part of sway); on hyprland ensure hyprctl (part of hyprland) is in PATH.
  3. On other Wayland compositors (GNOME/KDE), use an XWayland session or add a supported enumeration tool; this backend only enumerates via xrandr/swaymsg/hyprctl.
  4. Verify which session was detected by checking for the binaries: `command -v xrandr swaymsg hyprctl`.
  5. For headless CI, install xrandr alongside a virtual X server (xvfb + x11-xserver-utils).

Example fix

// before (Debian, X11 minimal)
# xrandr: command not found
// after
sudo apt install x11-xserver-utils
Defensive patterns

Strategy: fallback

Validate before calling

import { execFileSync } from 'child_process';
const has = (cmd) => { try { execFileSync('sh', ['-c', `command -v ${cmd}`], { stdio: 'ignore' }); return true; } catch { return false; } };
function displayEnumAvailable(session) {
  return session === 'x11' ? has('xrandr') : (has('swaymsg') || has('hyprctl'));
}

Try / catch

try {
  const displays = await listDisplays();
} catch (e) {
  if (e instanceof ExecError && e.message.includes('display enumeration needs')) {
    // degrade: return a single synthetic full-screen display
    return [{ index: 1, name: 'screen', points: await virtualScreenBounds(), main: true }];
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling list_displays on a system where: X11 lacks xrandr (minimal X installs, some containers), or Wayland is neither sway (no swaymsg) nor hyprland (no hyprctl) — e.g. GNOME Wayland without any of the tools, or tools absent from PATH.

Common situations: Docker/CI images with bare Xvfb and no xrandr; GNOME/KDE Wayland sessions (this backend supports sway/hyprctl enumeration there, and neither tool exists); stripped-down embedded or minimal arch installs.

Understand the failure class

Background: "not installed", "pip install", "required for": how missing-dependency errors surface across open-source libraries — this error's family across 34 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/8050b57c708651ac. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/plugins/computer-use/src/backends/linux.mjs:406

          displays.push({ index: i++, name: m[1], points: { x: Number(m[4]), y: Number(m[5]), w: Number(m[2]), h: Number(m[3]) }, pixels: { w: Number(m[2]), h: Number(m[3]) }, scale: 1, main: /primary/.test(m[0]) || i === 1 });
        }
        if (displays.length) return displays;
      }
      if (session === "wayland" && tools.swaymsg) {
        const r = await run("swaymsg", ["-t", "get_outputs", "-r"], { timeoutMs: 15_000 });
        const outs = tryJson(r.stdout, []);
        if (Array.isArray(outs) && outs.length) {
          return outs.map((o, i) => ({ index: i + 1, name: o.name, points: { x: o.rect?.x, y: o.rect?.y, w: o.rect?.width, h: o.rect?.height }, pixels: { w: o.current_mode?.width, h: o.current_mode?.height }, scale: o.scale ?? 1, main: i === 0 }));
        }
      }
      if (session === "wayland" && tools.hyprctl) {
        const r = await run("hyprctl", ["-j", "monitors"], { timeoutMs: 15_000 });
        const ms = tryJson(r.stdout, []);
        if (Array.isArray(ms) && ms.length) {
          return ms.map((o, i) => ({ index: i + 1, name: o.name, points: { x: o.x, y: o.y, w: o.width, h: o.height }, pixels: { w: o.width, h: o.height }, scale: o.scale ?? 1, main: !!o.main || i === 0 }));
        }
      }
      throw new ExecError("display enumeration needs xrandr (X11) or swaymsg/hyprctl (Wayland) — install one and retry");
    },
    switch_display: async ({ index }) => ({ activeDisplay: index ?? 1, note: "linux screenshots grab the compositor's virtual screen; per-display selection applies only where the shot tool supports it" }),
    list_apps: async () => {
      await probeSession();
      if (session === "x11" && tools.wmctrl) {
        const r = await runOk("wmctrl", ["-lx"], { timeoutMs: 15_000 });
        const seen = new Map();
        for (const line of r.stdout.split("\n")) {
          const parts = line.split(/\s+/);
          const wmClass = parts[2];
          if (wmClass) seen.set(wmClass, { name: wmClass.split(".")[0], wm_class: wmClass });
        }
        return { apps: [...seen.values()] };
      }
      if (session === "wayland" && (tools.swaymsg || tools.hyprctl)) {
        const w = await this.list_windows();
        const seen = new Map();
        for (const win of w.windows) {

View on GitHub (pinned to 73e0f67d83)