Hmbown/CodeWhale · error · ExecError

could not parse xdotool getmouselocation output

Error message

could not parse xdotool getmouselocation output: ${out}

What it means

The cursor_position handler for X11 runs `xdotool getmouselocation` and parses coordinates with the regex /x:(-?\d+)\s+y:(-?\d+)/. If the tool's output doesn't match (regex exec returns null), the backend throws this ExecError including the raw output. It protects callers from silently returning NaN/garbage coordinates.

Solutions

  1. Run `xdotool getmouselocation` manually and check its output/exit code
  2. Verify DISPLAY resolves to an accessible X server (e.g. `echo $DISPLAY; xset q`)
  3. Upgrade or reinstall xdotool to a standard version
  4. Inspect the raw output embedded in the error message to see what actually came back
Defensive patterns

Strategy: try-catch

Validate before calling

const probe = await run('xdotool', ['getmouselocation'], { timeoutMs: 5000 });
if (probe.code !== 0 || !/x:-?\d+\s+y:-?\d+/.test(probe.stdout)) throw new SkipError('xdotool output unusable');

Try / catch

try { pos = await cursorPosition(); } catch (e) { pos = null; log.warn('cursor position unavailable:', e.message); }

Prevention

When it happens

Trigger: Calling cursor_position on an X11 session where xdotool prints an error line (e.g. 'No such file or directory', 'failed to select inputs') or a localized/unexpected format instead of `x:123 y:456 ...`.

Common situations: xdotool present but cannot open the display (DISPLAY pointing at wrong screen); very old or patched xdotool versions with different output ordering; xdotool shimmed by a script that emits warnings first; running under XWayland where getmouselocation fails.

Related errors


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

Appendix: source

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

      const r = await run(cmd[0], cmd.slice(1), { timeoutMs: 10_000 });
      if (r.code !== 0) throw new ExecError("clipboard read failed", r);
      return { text: r.stdout, encoding: "utf8" };
    },
    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)