{"record":{"id":"08346b2c2be2e3ee","repo":"Hmbown/CodeWhale","slug":"linux-backend-needs-tool-for-purpose-install-it-and-retry","errorCode":null,"errorMessage":"linux backend needs \"${tool}\" for ${purpose} — install it and retry","messagePattern":"linux backend needs \"(.+?)\" for (.+?) — install it and retry","errorType":"exception","errorClass":"ExecError","httpStatus":null,"severity":"error","filePath":"crates/tui/plugins/computer-use/src/backends/linux.mjs","lineNumber":114,"sourceCode":"    const wayland = !!(process.env.WAYLAND_DISPLAY || process.env.XDG_SESSION_TYPE === \"wayland\");\n    const x11 = !!(process.env.DISPLAY || process.env.XDG_SESSION_TYPE === \"x11\");\n    session = wayland && !x11 ? \"wayland\" : x11 ? \"x11\" : null;\n    if (session === null) {\n      const e = new ExecError(\"no X11 ($DISPLAY) or Wayland ($WAYLAND_DISPLAY) session visible to this process — set DISPLAY or run inside the desktop session\");\n      e.code = \"no_session\";\n      throw e;\n    }\n    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\"]) {\n      tools[t] = await have(t);\n    }\n    tools.pyatspi = tools.python3 && (await run(\"python3\", [\"-c\", \"import pyatspi\"], { timeoutMs: 10_000 })).code === 0;\n    throwIfAborted();\n    probed = true;\n    return session;\n  }\n\n  function need(tool, purpose) {\n    if (!tools[tool]) throw new ExecError(`linux backend needs \"${tool}\" for ${purpose} — install it and retry`);\n  }\n\n  async function shotTool() {\n    if (session === \"wayland\") { need(\"grim\", \"screenshots on Wayland\"); return { cmd: \"grim\", base: [] }; }\n    if (session === \"x11\") {\n      if (tools.scrot) return { cmd: \"scrot\", base: [\"-z\"] };\n      need(\"import\", \"screenshots on X11 (imagemagick)\");\n      return { cmd: \"import\", base: [\"-window\", \"root\"] };\n    }\n    throw new ExecError(\"no X11 ($DISPLAY) or Wayland ($WAYLAND_DISPLAY) session visible to this process\");\n  }\n\n  /** Capture a PNG to `file`, optionally cropped to region [x,y,w,h] points. */\n  async function takeShot(file, region) {\n    outputPath(file);\n    const { cmd, base } = await shotTool();\n    let args = [...base];\n    if (cmd === \"grim\") {","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/plugins/computer-use/src/backends/linux.mjs#L96-L132","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\nawait backend.screenshot({ file: \"/tmp/s.png\" }); // linux backend needs \"grim\" for screenshots on Wayland\n// after\n// $ sudo apt install grim\nawait backend.screenshot({ file: \"/tmp/s.png\" });","handlingStrategy":"validation","validationCode":"const { execFile } = await import(\"node:child_process\");\nconst need = process.env.WAYLAND_DISPLAY ? \"grim\" : \"scrot\";\nawait new Promise((res, rej) => execFile(need, [\"--help\"], { stdio: \"ignore\" }, (e) => e && e.code !== 0 ? rej(new Error(`${need} missing`)) : res()));","typeGuard":"null","tryCatchPattern":"try { await backend.screenshot({ file }); }\ncatch (e) {\n  const m = String(e.message).match(/needs \"([^\"]+)\" for (.+?) —/);\n  if (m) throw new Error(`Install missing tool: ${m[1]} (${m[2]})`);\n  throw e;\n}","preventionTips":["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"],"tags":["missing-dependency","linux","screenshot","wayland","x11"],"backgroundTag":"missing-dependency","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T21:17:16.096Z"}