{"record":{"id":"1200f8870cf3cc58","repo":"Hmbown/CodeWhale","slug":"ydotool-exited-r-code-r-stderr-trim-slice-0-200","errorCode":null,"errorMessage":"ydotool exited ${r.code}: ${r.stderr.trim().slice(0, 200)}","messagePattern":"ydotool exited (.+?): (.+?)","errorType":"exception","errorClass":"ExecError","httpStatus":null,"severity":"error","filePath":"crates/tui/plugins/computer-use/src/backends/linux.mjs","lineNumber":164,"sourceCode":"  function recordingsDir() {\n    return path.resolve(process.env.CODEWHALE_CU_RECORDINGS_DIR || path.join(os.homedir(), \".codewhale-cu\", \"recordings\"));\n  }\n\n  async function xdotool(args, opts = {}) {\n    need(\"xdotool\", \"input on X11\");\n    throwIfAborted();\n    const r = await run(\"xdotool\", args, opts);\n    throwIfAborted();\n    if (r.code !== 0) throw new ExecError(`xdotool ${args[0]} exited ${r.code}: ${r.stderr.trim().slice(0, 200)}`, r);\n    return r.stdout.trim();\n  }\n\n  async function ydotool(args, opts = {}) {\n    need(\"ydotool\", \"input on Wayland (ydotool needs its daemon running: sudo ydotoold)\");\n    throwIfAborted();\n    const r = await run(\"ydotool\", args, opts);\n    throwIfAborted();\n    if (r.code !== 0) throw new ExecError(`ydotool exited ${r.code}: ${r.stderr.trim().slice(0, 200)}`, r);\n    return r.stdout.trim();\n  }\n\n  function xdotoolKey(text) {\n    return String(text).split(\"+\").map((p) => {\n      const k = p.trim().toLowerCase();\n      if (XKEYS[k]) return XKEYS[k];\n      if (/^f\\d{1,2}$/.test(k)) return k.toUpperCase();\n      return p.trim(); // pass through names already in xdotool form\n    }).join(\"+\");\n  }\n\n  async function waylandKey(text, { repeat = 1, holdMs = 0 } = {}) {\n    need(\"wtype\", \"key presses on Wayland\");\n    const parts = String(text).split(\"+\").map((part) => part.trim().toLowerCase());\n    const aliases = { control: \"ctrl\", meta: \"logo\", cmd: \"logo\", super: \"logo\" };\n    const modifiers = new Set([\"ctrl\", \"alt\", \"shift\", \"logo\", \"win\", \"altgr\", \"capslock\"]);\n    const rawKey = parts.pop();","sourceCodeStart":146,"sourceCodeEnd":182,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/plugins/computer-use/src/backends/linux.mjs#L146-L182","documentation":"Thrown by the ydotool wrapper when the ydotool subprocess exits non-zero on Wayland. ydotool injects input via uinput, so a non-zero exit almost always means its daemon (ydotoold) is not running or the user lacks uinput access. Stderr (first 200 chars) is included in the message.","triggerScenarios":"Input actions on Wayland routed through ydotool(args, opts) where the binary runs but fails: ydotoold daemon not running, /dev/uinput not writable by the user, or invalid arguments (e.g. unknown key code).","commonSituations":"Fresh Wayland setups where `sudo ydotoold` was never started; uinput kernel module or udev rules missing; running as a user not in the `input` group; ydotoold running as root while ydotool runs as another user with a mismatched socket.","solutions":["Start the daemon: `sudo ydotoold` (the need() hint in the source explicitly says ydotool requires it).","Add your user to the input/uinput group and reload udev rules, then re-login.","Verify manually: `ydotool key 30:1 30:0` — if it fails outside the library, fix the environment there.","Check /dev/uinput exists (`sudo modprobe uinput`) and the socket path matches between daemon and client.","Consider the wtype path for Wayland text/keys if your compositor supports the virtual-keyboard protocol."],"exampleFix":"// before (shell, one-time setup missing)\nydotool type 'hi'   # exits non-zero: no daemon\n// after\nsudo ydotoold &\nydotool type 'hi'","handlingStrategy":"try-catch","validationCode":"import { existsSync } from 'fs';\nfunction ydotoolReady() {\n  return existsSync('/dev/uinput') && process.getuid && daemonSocketReachable();\n}\nconst daemonSocketReachable = () => { try { accessSync('/run/ydotoold/socket'); return true; } catch { return false; } };","typeGuard":null,"tryCatchPattern":"try {\n  await ydotool(['key', code]);\n} catch (e) {\n  if (e instanceof ExecError && e.message.includes('ydotool exited')) {\n    // daemon or uinput missing — surface install/daemon instructions to the user\n    throw new Error('ydotool needs ydotoold running and /dev/uinput access: sudo ydotoold');\n  }\n  throw e;\n}","preventionTips":["Start ydotoold at boot (systemd unit) before using ydotool-based input.","Add the invoking user to the input/uinput group with proper udev rules.","Probe `ydotool` with a no-op gesture before real automations.","Keep daemon and client versions matched (socket path changes across versions).","Prefer wtype on compositors that support the virtual-keyboard protocol."],"tags":["subprocess","ydotool","wayland","input","uinput"],"backgroundTag":"missing-dependency","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}