{"record":{"id":"c0ba2ea3e1289084","repo":"stablyai/orca","slug":"modified-clicks-require-xdotool-on-an-x11-session","errorCode":null,"errorMessage":"modified clicks require xdotool on an X11 session","messagePattern":"modified clicks require xdotool on an X11 session","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"native/computer-use-linux/runtime.py","lineNumber":864,"sourceCode":"def click_modifier_keys(raw):\n    if raw is None:\n        return []\n    aliases = {\n        \"ctrl\": \"ctrl\", \"control\": \"ctrl\", \"cmdorctrl\": \"ctrl\", \"commandorcontrol\": \"ctrl\",\n        \"shift\": \"shift\", \"alt\": \"alt\", \"option\": \"alt\",\n        \"meta\": \"super\", \"super\": \"super\", \"win\": \"super\", \"cmd\": \"super\", \"command\": \"super\",\n    }\n    parts = [part.strip().lower() for part in str(raw).split(\"+\")]\n    if not parts or any(not part or part not in aliases for part in parts):\n        raise RuntimeError(\"click modifiers require modifier keys only\")\n    return list(dict.fromkeys(aliases[part] for part in parts))\n\n\ndef modified_click_at(x, y, button, count, modifier_keys):\n    xdotool = shutil.which(\"xdotool\")\n    is_wayland = os.environ.get(\"XDG_SESSION_TYPE\", \"\").lower() == \"wayland\"\n    if not xdotool or is_wayland:\n        raise RuntimeError(\"modified clicks require xdotool on an X11 session\")\n    button_number = {\"left\": \"1\", \"middle\": \"2\", \"right\": \"3\"}[button]\n    command = [xdotool, \"mousemove\", \"--sync\", str(round(x)), str(round(y))]\n    for modifier in modifier_keys:\n        command.extend([\"keydown\", modifier])\n    command.extend([\"click\", \"--repeat\", str(count), \"--delay\", \"35\", button_number])\n    for modifier in reversed(modifier_keys):\n        command.extend([\"keyup\", modifier])\n    try:\n        subprocess.run(command, check=True, timeout=5)\n    finally:\n        subprocess.run(\n            [xdotool, *[item for modifier in reversed(modifier_keys) for item in (\"keyup\", modifier)]],\n            check=False,\n            timeout=2,\n        )\n\n\ndef scroll_at(x, y, direction, pages):","sourceCodeStart":846,"sourceCodeEnd":882,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/native/computer-use-linux/runtime.py#L846-L882","documentation":"Raised by modified_click_at (runtime.py:864) when xdotool is not on PATH OR XDG_SESSION_TYPE indicates a Wayland session. Modified (modifier-key) clicks cannot be synthesized via AT-SPI alone, so the code shells out to xdotool — but xdotool is an X11 tool and does not control Wayland clients, hence the combined guard.","triggerScenarios":"A 'click' operation with a non-empty modifiers string reaches the modified-click path, and either xdotool is not installed (shutil.which returns None) or the desktop session is Wayland (XDG_SESSION_TYPE=wayland).","commonSituations":"Running on a modern Fedora/Ubuntu default install (Wayland) without xdotool; a minimal container/CI image lacking xdotool; an X11 system where the user forgot to apt install xdotool.","solutions":["Install xdotool (apt install xdotool / dnf install xdotool) AND run under an X11/Xorg session (log out, pick 'Xorg' on the display manager).","Drop the modifiers and perform the click without them, then send the modifier combination separately via press_key if the workflow allows.","On Wayland, switch to an Xorg session or use a Wayland-native automation tool outside this runtime."],"exampleFix":"# before: Wayland session, no xdotool, modifier click fails\n# after (shell): install + use X11\nsudo apt install xdotool\n# then log in via 'Xorg' on the display manager","handlingStrategy":"fallback","validationCode":"import shutil, os\n\ndef can_modified_click():\n    return bool(shutil.which(\"xdotool\")) and os.environ.get(\"XDG_SESSION_TYPE\", \"\").lower() != \"wayland\"\n\nif not can_modified_click():\n    op.pop(\"modifiers\", None)  # degrade to plain click","typeGuard":"import shutil, os\n\ndef supports_modifier_clicks() -> bool:\n    return bool(shutil.which(\"xdotool\")) and os.environ.get(\"XDG_SESSION_TYPE\", \"\").lower() != \"wayland\"","tryCatchPattern":"try:\n    run_operation(op)\nexcept RuntimeError as e:\n    if \"modified clicks require xdotool\" in str(e):\n        op.pop(\"modifiers\", None)\n        run_operation(op)  # retry without modifiers\n    else:\n        raise","preventionTips":["Install xdotool and run under X11 if you need modifier clicks.","Probe xdotool + session type at startup and disable modifier-click UI when unsupported.","On Wayland, prefer separate press_key actions over modified clicks."],"tags":["environment","computer-use","xdotool","wayland","x11","python"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}