Hmbown/CodeWhale · error · ExecError
linux backend needs " " for — install it and retry
Error message
linux backend needs "${tool}" for ${purpose} — install it and retry What it means
The Linux backend shells out to external tools (grim, scrot, import, etc.) for screenshots and input. `need(tool, purpose)` checks the tool-availability probe and throws when a required tool is missing, naming the tool and the purpose so the user knows what to install.
Solutions
- Install the named tool: on Wayland `apt install grim`; on X11 `apt install scrot` or `imagemagick`.
- Match the tool to your session type (grim for Wayland, scrot/import for X11).
- In containers, add the tool to the image and ensure the process has access to the display.
Example fix
// before
await backend.screenshot({ file: "/tmp/s.png" }); // linux backend needs "grim" for screenshots on Wayland
// after
// $ sudo apt install grim
await backend.screenshot({ file: "/tmp/s.png" }); Defensive patterns
Strategy: validation
Validate before calling
const { execFile } = await import("node:child_process");
const need = process.env.WAYLAND_DISPLAY ? "grim" : "scrot";
await new Promise((res, rej) => execFile(need, ["--help"], { stdio: "ignore" }, (e) => e && e.code !== 0 ? rej(new Error(`${need} missing`)) : res())); Type guard
null
Try / catch
try { await backend.screenshot({ file }); }
catch (e) {
const m = String(e.message).match(/needs "([^"]+)" for (.+?) —/);
if (m) throw new Error(`Install missing tool: ${m[1]} (${m[2]})`);
throw e;
} Prevention
- Install grim on Wayland and scrot/imagemagick on X11 in your setup script
- Probe tool availability at app startup
- Keep screenshot utilities in your Docker/CI images
When it happens
Trigger: Taking a screenshot on Wayland without `grim`, or on X11 without `scrot`/`import` (imagemagick), or any other action whose backing tool is not installed (backends/linux.mjs:114).
Common situations: Fresh Linux installs or slim Docker images without screenshot utilities; Wayland sessions where X11 tools like scrot don't apply and grim was never installed; switching from X11 to Wayland between runs.
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
- clipboard read failed
- cursor position needs an X11 session in this build
- PRIMARY selection busy or unavailable
- scroll on Wayland is not available in this build; use…
- AT-SPI walk failed — is python3-pyatspi installed and the…
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/08346b2c2be2e3ee.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/plugins/computer-use/src/backends/linux.mjs:114
const wayland = !!(process.env.WAYLAND_DISPLAY || process.env.XDG_SESSION_TYPE === "wayland");
const x11 = !!(process.env.DISPLAY || process.env.XDG_SESSION_TYPE === "x11");
session = wayland && !x11 ? "wayland" : x11 ? "x11" : null;
if (session === null) {
const e = new ExecError("no X11 ($DISPLAY) or Wayland ($WAYLAND_DISPLAY) session visible to this process — set DISPLAY or run inside the desktop session");
e.code = "no_session";
throw e;
}
for (const t of ["xdotool", "wmctrl", "scrot", "import", "grim", "slurp", "wtype", "ydotool", "wf-recorder", "ffmpeg", "xclip", "xsel", "wl-copy", "wl-paste", "python3", "xrandr", "swaymsg", "hyprctl"]) {
tools[t] = await have(t);
}
tools.pyatspi = tools.python3 && (await run("python3", ["-c", "import pyatspi"], { timeoutMs: 10_000 })).code === 0;
throwIfAborted();
probed = true;
return session;
}
function need(tool, purpose) {
if (!tools[tool]) throw new ExecError(`linux backend needs "${tool}" for ${purpose} — install it and retry`);
}
async function shotTool() {
if (session === "wayland") { need("grim", "screenshots on Wayland"); return { cmd: "grim", base: [] }; }
if (session === "x11") {
if (tools.scrot) return { cmd: "scrot", base: ["-z"] };
need("import", "screenshots on X11 (imagemagick)");
return { cmd: "import", base: ["-window", "root"] };
}
throw new ExecError("no X11 ($DISPLAY) or Wayland ($WAYLAND_DISPLAY) session visible to this process");
}
/** Capture a PNG to `file`, optionally cropped to region [x,y,w,h] points. */
async function takeShot(file, region) {
outputPath(file);
const { cmd, base } = await shotTool();
let args = [...base];
if (cmd === "grim") {View on GitHub (pinned to 73e0f67d83)