stablyai/orca · error · RuntimeError

window_not_focused: keyboard input requires the target…

Error message

window_not_focused: keyboard input requires the target window to be focused; restoreWindow was requested but the target window is still not focused; bring it forward manually or check desktop permissions

What it means

Raised by require_keyboard_focus (runtime.py:180-191) when operation.get('restoreWindow') was truthy, the window did not have the AT-SPI ACTIVE state, and a 500ms polling loop (10× 50ms checks) failed to observe the window become ACTIVE. restore_window was attempted (in run_operation line 1049-1051 before require_keyboard_focus) via grab_focus or xdotool windowactivate, but the compositor still did not grant active focus — synthetic keyboard input would go to the wrong window, so the bridge refuses rather than type into the wrong app.

Solutions

  1. Manually bring the target window to the foreground (click it, Alt-Tab to it) and retry without restoreWindow.
  2. On Wayland, ensure the AT-SPI bridge and Orca have permission to grab focus (focus-stealing-prevention disabled or the right xdg-desktop-portal).
  3. Increase robustness by retrying the operation after a longer settle, or chain multiple restore attempts with xdotool.
  4. If the window is on another workspace, switch to that workspace first (wmctrl -s or xdotool set_desktop_for_window).
Defensive patterns

Strategy: fallback

Validate before calling

# Pre-check focus state before requiring keyboard focus
if not has_state(window, Atspi.StateType.ACTIVE) and not operation.get('restoreWindow'):
    operation['restoreWindow'] = True  # let the bridge attempt activation
# also pre-check the window is restorable (not on another workspace, etc.)

Type guard

def window_has_focus(window) -> bool:
    return has_state(window, Atspi.StateType.ACTIVE)

Try / catch

try:
    require_keyboard_focus(window, operation)
except RuntimeError as exc:
    if 'still not focused' in str(exc):
        # manual fallback: prompt user to bring window forward, or switch workspace
        raise SystemExit(f'Could not activate {name_of(window)}. Bring it forward manually and retry.')
    raise

Prevention

When it happens

Trigger: type_text/press_key/hotkey/paste_text called with restoreWindow:true on a window that another app's focus-stealing, a fullscreen/always-on-top window, minimized state, or lacking focus permissions prevents from becoming ACTIVE within 500ms. restore_window's grab_focus returned false or xdotool's windowactivate didn't take effect.

Common situations: GNOME/Mutter focus-stealing-prevention blocking grab_focus; a fullscreen game or always-on-top dialog keeping focus; the target window on a different virtual desktop; the user's session lacks permission to programmatically activate windows (Wayland without the right portal).

Related errors


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

Appendix: source

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

        ["xdotool", "search", "--pid", str(pid), "windowactivate", "--sync"],
        check=False,
        stdout=subprocess.DEVNULL,
        stderr=subprocess.DEVNULL,
    )


def require_keyboard_focus(window, operation):
    if has_state(window, Atspi.StateType.ACTIVE):
        return
    if operation.get("restoreWindow"):
        deadline = time.monotonic() + 0.5
        while time.monotonic() < deadline:
            if has_state(window, Atspi.StateType.ACTIVE):
                return
            time.sleep(0.05)
        if has_state(window, Atspi.StateType.ACTIVE):
            return
        raise RuntimeError("window_not_focused: keyboard input requires the target window to be focused; restoreWindow was requested but the target window is still not focused; bring it forward manually or check desktop permissions")
    raise RuntimeError("window_not_focused: keyboard input requires the target window to be focused; retry with --restore-window")


def app_matches(app, query):
    needle = str(query or "").strip().lower()
    if not needle:
        return False
    if needle.startswith("pid:"):
        requested_pid = parse_positive_pid(needle[4:])
        return requested_pid is not None and pid_of(app) == requested_pid
    if needle.isdigit() and int(needle) > 0 and pid_of(app) == int(needle):
        return True
    haystacks = [name_of(app).lower()] + [name_of(window).lower() for _, window in windows_for(app)]
    return any(value == needle or needle in value for value in haystacks)


def parse_positive_pid(value):
    return int(value) if value.isdigit() and int(value) > 0 else None

View on GitHub (pinned to 1136503c6a)