stablyai/orca · warning · RuntimeError
window_not_focused: keyboard input requires the target…
Error message
window_not_focused: keyboard input requires the target window to be focused; retry with --restore-window
What it means
Raised by require_keyboard_focus (runtime.py:192) when the target window is not ACTIVE and the caller did NOT set operation.get('restoreWindow'). This is the 'hint to retry correctly' variant: the bridge detected keyboard input would go to the wrong window and tells the caller exactly how to fix the next attempt — pass restoreWindow so the bridge attempts to activate the window first (the path in error 493). Contrast with 493 which fires when restoreWindow WAS set but failed.
Solutions
- Retry the same operation with restoreWindow:true — the bridge will attempt grab_focus/xdotool before requiring focus.
- Always pair keyboard operations with restoreWindow unless you've just verified focus via get-app-state's focusedSummary.
- After a click on app A, optionally verify with a fresh snapshot that A holds ACTIVE before typing.
- If restoreWindow still fails (error 493), restore focus manually.
Example fix
// before — keyboard op without restore
{ "app": "Firefox", "tool": "type_text", "text": "hi" }
// after — let the bridge activate the window first
{ "app": "Firefox", "tool": "type_text", "text": "hi", "restoreWindow": true } Defensive patterns
Strategy: retry
Validate before calling
# If window lacks focus, set restoreWindow before dispatch
if not window_has_focus(window):
operation = {**operation, 'restoreWindow': True} Type guard
def needs_restore_window(window, operation) -> bool:
return not has_state(window, Atspi.StateType.ACTIVE) and not operation.get('restoreWindow') Try / catch
try:
run_operation(operation)
except RuntimeError as exc:
if 'retry with --restore-window' in str(exc):
operation = {**operation, 'restoreWindow': True}
run_operation(operation)
else:
raise Prevention
- Always pair keyboard operations (type_text, press_key, hotkey, paste_text) with restoreWindow:true.
- After clicks that don't change focus (accessibility-path clicks), verify focus via get-app-state before typing.
- Wrap keyboard ops in a retry that adds restoreWindow on this specific message.
When it happens
Trigger: type_text/press_key/hotkey/paste_text operation dispatched while another window has focus, and the operation JSON omitted 'restoreWindow' (or set it false). The AT-SPI ACTIVE state belongs to a different window, so synthetic keys (Atspi.generate_keyboard_event) would land in the wrong app.
Common situations: Agent performed a click in app A (which may or may not have activated it depending on the click path), then immediately called type_text without restoreWindow, and the previously-focused app B still holds ACTIVE. Common after accessibility-path clicks that don't change focus.
Related errors
- window_not_focused: keyboard input requires the target…
- No top-level AT-SPI window is available for
- windowId is not supported by the Linux AT-SPI provider; use…
- windowNotFound(" ")
- appBlocked(" ")
AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12).
Data as JSON: /api/errors/cc970a2b26fa38ad.
Report an issue: GitHub.
Appendix: source
Thrown at native/computer-use-linux/runtime.py:192
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)