stablyai/orca · error · RuntimeError

modified clicks require xdotool on an X11 session

Error message

modified clicks require xdotool on an X11 session

What it means

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.

Source

Thrown at native/computer-use-linux/runtime.py:864

def click_modifier_keys(raw):
    if raw is None:
        return []
    aliases = {
        "ctrl": "ctrl", "control": "ctrl", "cmdorctrl": "ctrl", "commandorcontrol": "ctrl",
        "shift": "shift", "alt": "alt", "option": "alt",
        "meta": "super", "super": "super", "win": "super", "cmd": "super", "command": "super",
    }
    parts = [part.strip().lower() for part in str(raw).split("+")]
    if not parts or any(not part or part not in aliases for part in parts):
        raise RuntimeError("click modifiers require modifier keys only")
    return list(dict.fromkeys(aliases[part] for part in parts))


def modified_click_at(x, y, button, count, modifier_keys):
    xdotool = shutil.which("xdotool")
    is_wayland = os.environ.get("XDG_SESSION_TYPE", "").lower() == "wayland"
    if not xdotool or is_wayland:
        raise RuntimeError("modified clicks require xdotool on an X11 session")
    button_number = {"left": "1", "middle": "2", "right": "3"}[button]
    command = [xdotool, "mousemove", "--sync", str(round(x)), str(round(y))]
    for modifier in modifier_keys:
        command.extend(["keydown", modifier])
    command.extend(["click", "--repeat", str(count), "--delay", "35", button_number])
    for modifier in reversed(modifier_keys):
        command.extend(["keyup", modifier])
    try:
        subprocess.run(command, check=True, timeout=5)
    finally:
        subprocess.run(
            [xdotool, *[item for modifier in reversed(modifier_keys) for item in ("keyup", modifier)]],
            check=False,
            timeout=2,
        )


def scroll_at(x, y, direction, pages):

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Install xdotool (apt install xdotool / dnf install xdotool) AND run under an X11/Xorg session (log out, pick 'Xorg' on the display manager).
  2. Drop the modifiers and perform the click without them, then send the modifier combination separately via press_key if the workflow allows.
  3. On Wayland, switch to an Xorg session or use a Wayland-native automation tool outside this runtime.

Example fix

# before: Wayland session, no xdotool, modifier click fails
# after (shell): install + use X11
sudo apt install xdotool
# then log in via 'Xorg' on the display manager
Defensive patterns

Strategy: fallback

Validate before calling

import shutil, os

def can_modified_click():
    return bool(shutil.which("xdotool")) and os.environ.get("XDG_SESSION_TYPE", "").lower() != "wayland"

if not can_modified_click():
    op.pop("modifiers", None)  # degrade to plain click

Type guard

import shutil, os

def supports_modifier_clicks() -> bool:
    return bool(shutil.which("xdotool")) and os.environ.get("XDG_SESSION_TYPE", "").lower() != "wayland"

Try / catch

try:
    run_operation(op)
except RuntimeError as e:
    if "modified clicks require xdotool" in str(e):
        op.pop("modifiers", None)
        run_operation(op)  # retry without modifiers
    else:
        raise

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12). Data as JSON: /api/errors/c0ba2ea3e1289084. Report an issue: GitHub.