{"record":{"id":"fa1d152aabe26858","repo":"Hmbown/CodeWhale","slug":"list-apps-needs-wmctrl-x11-or-swaymsg-hyprctl-wayland","errorCode":null,"errorMessage":"list_apps needs wmctrl (X11) or swaymsg/hyprctl (Wayland)","messagePattern":"list_apps needs wmctrl \\(X11\\) or swaymsg/hyprctl \\(Wayland\\)","errorType":"exception","errorClass":"ExecError","httpStatus":null,"severity":"error","filePath":"crates/tui/plugins/computer-use/src/backends/linux.mjs","lineNumber":430,"sourceCode":"        const r = await runOk(\"wmctrl\", [\"-lx\"], { timeoutMs: 15_000 });\n        const seen = new Map();\n        for (const line of r.stdout.split(\"\\n\")) {\n          const parts = line.split(/\\s+/);\n          const wmClass = parts[2];\n          if (wmClass) seen.set(wmClass, { name: wmClass.split(\".\")[0], wm_class: wmClass });\n        }\n        return { apps: [...seen.values()] };\n      }\n      if (session === \"wayland\" && (tools.swaymsg || tools.hyprctl)) {\n        const w = await this.list_windows();\n        const seen = new Map();\n        for (const win of w.windows) {\n          const cls = win.wm_class || win.app_id;\n          if (cls) seen.set(cls, { name: cls, wm_class: cls });\n        }\n        return { apps: [...seen.values()] };\n      }\n      throw new ExecError(\"list_apps needs wmctrl (X11) or swaymsg/hyprctl (Wayland)\");\n    },\n    list_windows: async (args = {}) => {\n      rejectWindowSelectors(args);\n      await probeSession();\n      if (session === \"x11\" && tools.wmctrl) {\n        const r = await runOk(\"wmctrl\", [\"-lGx\"], { timeoutMs: 15_000 });\n        const windows = [];\n        for (const line of r.stdout.split(\"\\n\")) {\n          const m = /^(\\S+)\\s+(-?\\d+)\\s+(-?\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\S+)\\s+(\\S+)\\s+(.*)$/.exec(line);\n          if (m) windows.push({ id: m[1], desktop: m[2], position: { x: Number(m[3]), y: Number(m[4]) }, size: { w: Number(m[5]), h: Number(m[6]) }, wm_class: m[7], title: m[8] });\n        }\n        return { windows };\n      }\n      if (session === \"wayland\" && tools.swaymsg) {\n        const r = await run(\"swaymsg\", [\"-t\", \"get_tree\", \"-r\"], { timeoutMs: 15_000 });\n        const windows = [];\n        const walk = (n) => {\n          if (n.type === \"con\" && n.name) windows.push({ id: String(n.id), title: n.name, wm_class: n.app_id ?? null, position: { x: n.rect?.x, y: n.rect?.y }, size: { w: n.rect?.width, h: n.rect?.height }, focused: !!n.focused });","sourceCodeStart":412,"sourceCodeEnd":448,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/plugins/computer-use/src/backends/linux.mjs#L412-L448","documentation":"Thrown by list_apps when no supported application-enumeration tool exists for the session: wmctrl on X11, or swaymsg/hyprctl on Wayland. Unlike the AT-SPI path, the code here requires one of these window-manager tools; without them it throws this directive message instead of returning results.","triggerScenarios":"Calling list_apps when: X11 session without wmctrl installed; Wayland session where probeSession succeeded but neither swaymsg nor hyprctl is available (or the sway/hypr branches were skipped because the tools are missing).","commonSituations":"Fresh desktop installs without wmctrl; Wayland compositors other than sway/hyprland (GNOME, KDE) where none of the three tools apply; containerized sessions with no window-manager tooling.","solutions":["On X11 install wmctrl (`sudo apt install wmctrl`) and verify with `wmctrl -lx`.","On sway/hyprland ensure swaymsg/hyprctl are in PATH (they ship with the compositor).","On other Wayland compositors, this backend cannot list apps via this path — switch to the AT-SPI-backed flow or run an X11/XWayland session with wmctrl.","Check that probeSession detected the expected session; a wrong session guess makes it look for the wrong tool.","Confirm the binary is on PATH for the same user running the plugin."],"exampleFix":"// before (X11)\n# wmctrl: command not found\n// after\nsudo apt install wmctrl","handlingStrategy":"fallback","validationCode":"import { execFileSync } from 'child_process';\nconst has = (cmd) => { try { execFileSync('sh', ['-c', `command -v ${cmd}`], { stdio: 'ignore' }); return true; } catch { return false; } };\nfunction appsAvailable(session) {\n  return session === 'x11' ? has('wmctrl') : (has('swaymsg') || has('hyprctl'));\n}","typeGuard":null,"tryCatchPattern":"try {\n  const { apps } = await listApps();\n} catch (e) {\n  if (e instanceof ExecError && e.message.startsWith('list_apps needs')) {\n    // fallback: empty app list with a capability note, or route to AT-SPI flow\n    return { apps: [], note: 'install wmctrl/swaymsg/hyprctl for app enumeration' };\n  }\n  throw e;\n}","preventionTips":["Install wmctrl on X11 images (`sudo apt install wmctrl`).","Verify swaymsg/hyprctl are present and on PATH under sway/hyprland.","Run the plugin with the desktop session's PATH/user so tools are found.","Document the GNOME/KDE-Wayland limitation and route those users to the AT-SPI flow.","Add a preflight capability check before exposing list_apps to callers."],"tags":["dependency","linux","windows","wmctrl"],"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"}