Hmbown/CodeWhale · warning · ExecError

cursor position needs an X11 session in this build

Error message

cursor position needs an X11 session in this build

What it means

cursor_position is only implemented for X11 in this backend build. On Wayland sessions the handler falls through to a throw saying an X11 session is required. This is a deliberate fail-closed limit, since Wayland intentionally blocks global pointer-position queries from clients.

Solutions

  1. Log in with an 'Xorg' session (GNOME on Xorg, Plasma X11) if cursor position is required
  2. Read the cursor via a Wayland-native route, e.g. an overlay/compositor extension or `dotool`/compositor-specific IPC
  3. Skip cursor-position features on Wayland and use screenshot+coordinate workflows instead

Example fix

// before
const pos = await backend.cursor_position();
// after
const session = await detectSession();
const pos = session === 'x11' ? await backend.cursor_position() : null; // unsupported on Wayland
Defensive patterns

Strategy: validation

Validate before calling

if (sessionType !== 'x11') throw new SkipError('cursor_position requires X11; session is ' + sessionType);

Type guard

const supportsCursorPosition = (backend) => backend.session === 'x11';

Prevention

When it happens

Trigger: Calling cursor_position while probeSession detected a Wayland session (wl-* tooling) rather than X11.

Common situations: GNOME/KDE Wayland desktops (the default on modern distros); apps forced onto Wayland via environment variables; hybrid setups where the app sees XWayland but the session reported Wayland.

Understand the failure class

Background: "unsupported platform" / "not supported on this platform" errors: what they mean and how to fix them — this error's family across 47 libraries.

Related errors


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

Appendix: source

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

    },
    write_clipboard: async ({ text }) => {
      await probeSession();
      const cmd = session === "x11"
        ? (tools.xclip ? ["xclip", "-selection", "clipboard"] : ["xsel", "--clipboard", "--input"])
        : ["wl-copy"];
      need(cmd[0], "clipboard write");
      spawnDetached(cmd[0], cmd.slice(1), String(text ?? ""), true);
      return { written: String(text ?? "").length };
    },
    cursor_position: async () => {
      await probeSession();
      if (session === "x11") {
        const out = await xdotool(["getmouselocation"]);
        const m = /x:(-?\d+)\s+y:(-?\d+)/.exec(out);
        if (!m) throw new ExecError(`could not parse xdotool getmouselocation output: ${out}`);
        return { x: Number(m[1]), y: Number(m[2]) };
      }
      throw new ExecError("cursor position needs an X11 session in this build");
    },
    recordingStart: async () => {
      throw Object.assign(new ExecError("Recording is unavailable on this platform until the recorder has session-owned cleanup. Use screenshots instead."), { code: "owned_recording_unavailable" });
    },
    recordingStop: async ({ id }) => { throw new ExecError(`unknown recording "${id}"`); },
    recordingStatus: ({ id }) => ({ id, running: false }),
    recordingList: async () => {
      const dir = recordingsDir();
      const out = fs.existsSync(dir)
        ? fs.readdirSync(dir).filter((f) => /\.(mp4|mkv|png)$/i.test(f)).map((f) => {
            const st = fs.statSync(path.join(dir, f));
            return { file: path.join(dir, f), bytes: st.size, modifiedAt: st.mtime.toISOString() };
          }).sort((a, b) => b.modifiedAt.localeCompare(a.modifiedAt)).slice(0, 50)
        : [];
      return { dir, recordings: out, running: [] };
    },
  };

View on GitHub (pinned to 73e0f67d83)