Skip to content

Error reference

A failed call sets isError and returns {"error": <name>, "detail": <upstream detail>}, with "may_have_sent": true added when key or type_text failed after some of its input went out, or may have. The names below are a stable contract; the detail carries niri’s, Noctalia’s or a program’s own message, so quote it when you report a problem. This list is checked against ErrorName in src/error.rs when the site builds.

A mistake in the arguments isn’t one of these: it comes back as plain text starting failed to deserialize parameters: or invalid arguments:, and nothing was done.

An agent should stop and tell you when it gets one of these. Calling the action again won’t help.

Cause: the stop flag is set for this niri instance, by the stop key or niri-computer-use stop. A stop during an action cancels it with this error. A niri action whose request had already gone out, a Noctalia command, an activate_element or set_element_text whose call to the app had, key or type_text once a stroke had, or a pointer tool once its first motion or held modifier had, isn’t an error: it ends uncertain, with the stop in detail and accepted: true if niri, Noctalia or the app had answered or a stroke had been typed, null if not. A paste ends with this error either way, and a key it had already sent may have taken effect. While the lease is held, a runtime directory that can’t be read counts as stopped and the lease is given up; an action that finds it unreadable as it starts fails with upstream_error.

What to do: when you want agents to act again, run niri-computer-use resume. If status shows input_dirty, run niri-computer-use recover first.

Cause: the input-dirty marker exists: an earlier input call couldn’t confirm that every key and button was released, for example because the server was killed mid-click. The detail names the marker’s operation and phase.

What to do: if the detail says a cancelled call’s input is still finishing, that input removes the marker itself within seconds; try again then. Otherwise the agent calls release_desktop, since recover needs the lease, and you run niri-computer-use recover and answer its question. See Safety.

Cause: the screen is locked, or neither logind nor Noctalia can say that it isn’t. status shows lock.state and, under lock.logind_error, why logind couldn’t answer.

What to do: unlock the screen. If it is unlocked and lock.state is unknown, run niri as niri --session or run Noctalia; see Troubleshooting.

Cause: an action’s request, input or command must go out within 250 ms of when the server’s last read of the lock state began, since a lock after that read could be missed. The desktop answered so slowly that the call couldn’t go out in time, so usually nothing was sent; see below. The screen may well be unlocked.

What to do: try the call once more. If it comes back lock_stale again, the desktop is under load; tell the user.

With may_have_sent: true, part of a key or type_text call went out before the refusal, and the detail says how much instead of asking for another try. Repeating the call would type that part again; take a screenshot to see what arrived before sending the rest.

Cause: the Wayland display isn’t served by the niri on niri’s socket, so WAYLAND_DISPLAY and NIRI_SOCKET name two different compositors. The server checks this right before each input and screenshot, and when acquire_desktop starts watching the clipboard, and refuses them, because grim and the clipboard’s watcher would reach the other compositor; clipboard_read then fails with this error until the lease is released and taken again. A niri that restarted while the server ran gives this too when another compositor took over the display. status shows the current check under display_error.

What to do: pass the agent’s client both variables from the same niri session, or neither, and start a new agent session.

Cause: another server holds the lease on this niri instance. The detail names its PID, its client and since when, or says it is still being taken, as while its user is asked to allow it.

What to do: finish or close the other agent’s session; its server gives the lease up when it releases or exits. The stop key also takes the lease back.

Cause: the user’s policy turns on confirm_lease, and the user chose Deny on the desktop’s prompt, dismissed it, or didn’t answer within 60 seconds. The detail says which. No lease was granted.

What to do: don’t call acquire_desktop again on your own. Ask the user whether they want you to use the desktop, and call it again only once they say yes, so they can expect the prompt.

Cause: this build doesn’t support the running niri’s major or minor version, niri sent events this build can’t parse, or the policy file is invalid. The detail says which.

What to do: for niri’s version, use a build that matches it (status shows niri.version and niri.ipc_crate). For the policy file, fix the error status shows under policy.error, then restart the agent’s session.

Cause: the window with keyboard focus belongs to an app on deny_input_app_ids, or for elements, the window asked about does, or for activate_element, set_element_text and a pointer tool aimed at an element, the element’s window does, or for close_window and niri_action’s CloseWindow, the window it would close does.

What to do: nothing, if you meant it. Otherwise remove the app from the policy file and restart the agent’s session.

Cause: a pointer tool was called while niri’s outputs are a setup no live test covers: more than one enabled output, or a monitor with a transform other than Normal. The detail lists the enabled outputs and their transforms.

What to do: the pointer tools work with one monitor at transform Normal. Structured actions and the keyboard work with any setup.

Cause: the keyboard can’t send the requested input safely: a control character the layout lacks, a key name that is no keysym or a keysym none of your layouts has, a symbol the layout lacks when the keymap has no spare keycode for it, or a keymap from niri the server can’t use. Nothing was typed.

What to do: the detail says which limit was hit. Paste text the layout can’t type with via: "paste".

Cause: an action tool or clipboard_read was called without holding the lease, acquire_desktop’s lease was released, taken back by a stop or ended with its session before the call finished, or the lease file was removed or replaced before or during the action, so another server may hold the lease. The server then gives its lease up, and cancels an action that was running; one whose request or call had already gone out ends uncertain instead, as for a stop.

What to do: call acquire_desktop first, if you asked the agent to act.

Cause: launch named a preset the policy file doesn’t have. The detail lists the names it has.

What to do: use one of those names, or add a preset and restart the agent’s session.

Cause: a pointer tool’s screenshot_ref, or the one a screenshot zooms into, can’t be used. The detail starts with the reason: unknown_ref (not a screenshot of this lease, or niri’s event stream reconnected since), expired (over 60 seconds old), output_changed (the output moved, resized, changed scale or transform, or is gone) or out_of_bounds (the pixel is outside the image).

What to do: take a new screenshot and aim from it; for out_of_bounds, use a pixel inside the image.

Cause: a keyboard tool’s expect doesn’t match the window with keyboard focus, or for activate_element and set_element_text, the element’s window doesn’t have keyboard focus or expect doesn’t name it. The detail says what has focus. Often a dialog or notification took focus; an open Noctalia panel leaves no window focused. Nothing was typed or sent.

What to do: look at desktop_state and a screenshot, focus the intended window with focus if that is what was meant, then type again.

Cause: type_text with over 1000 characters, or over 1 MiB with via: "paste", or set_element_text with over 64 KiB of UTF-8. Nothing was typed or set.

What to do: split the text into calls of at most 1000 characters, or pass via: "paste".

Cause: a paste (type_text with via: "paste") couldn’t save the clipboard whole before replacing it: every type it offers, over 16 MiB in all, or not read within two seconds. Or the clipboard holds what its owner marked as a secret (x-kde-passwordManagerHint), or something else was copied while it was being saved. Nothing changed. An earlier paste’s text, which a kept outcome leaves on the clipboard marked as a secret, isn’t refused: the next paste replaces it and reports clipboard: cleared.

What to do: type the text with type_text and via: "keys" instead, in parts of at most 1000 characters with submit: false. Only if the text was meant to be submitted does the last part take submit: true, after the others went out whole: a paste never submits, so the fallback mustn’t either. Typed text can’t hold line breaks, so text with several lines needs another way; the error’s detail tells the agent to ask the user.

Cause: screenshot was given save_path while saving is off: the policy file sets capture_dir = "" or is invalid, or the capture directory starts with ~/ and HOME isn’t set.

What to do: fix the policy file or set a capture_dir and restart the agent’s session, or take the screenshot without saving.

Cause: shell_panel named a panel other than control-center, wallpaper or tray-drawer. Nothing was sent.

What to do: use one of those three. The others, such as the session menu and the launcher, are deliberately out of reach.

Cause: the window’s app isn’t on the accessibility bus, or has no accessible window that is this one.

What to do: aim at screenshot pixels for that window.

Cause: the app has several accessible windows that could be the one asked about, the one the size of the window asked about has another of its windows’ titles, or another of the app’s windows could be that accessible window as well: it has no accessible window of its own size, and the titles don’t tell the two apart (the same title, as two “Untitled” documents have, or one of them unknown). The server won’t guess.

What to do: aim at screenshot pixels for that window.

Cause: an element ref’s window, app or element is gone; the element is now another kind of element or sits elsewhere in its window’s tree; the element, or an element it sits in, has another name than when elements listed it (an app may reuse an object for another record, and where a list names its rows after their records, that catches a reused row); or the ref isn’t from this lease. A list’s row named after its record, as GTK 4’s labelled list items and Qt Quick’s item delegates are, makes its controls’ refs stale once the row shows another record. In GTK 4’s list view, a row’s ref goes stale once a record above it was removed or the list scrolled. activate_element and set_element_text compare the element’s own name too, so a button that relabelled itself when activated, as a counter does, is stale for the next one. The pointer tools compare only the names of the elements it sits in, except for a ref to a named list row itself, whose own name binds its record and is compared too.

What to do: call elements again and aim from its new list.

Cause: the element is still there but can’t be aimed at now. The detail starts with frame_size_mismatch (the app’s coordinates for this window can’t be trusted), not_showing, tiled (niri gives its tiled window no place on screen), empty, outside_screenshot (its centre is outside the screenshot the pointer aims through, or its window is on another output than the screenshot) or, for activate_element, action_unmapped (not exactly one of the app’s actions is known to have the name the element lists the chosen one by, so the server can’t tell which action it would run; nothing was sent).

What to do: for frame_size_mismatch, aim at a screenshot pixel or use activate_element instead. For not_showing, bring it into view first, close niri’s overview if it is open, or, when the detail says a tiled window as large as the output covers it, focus the element’s window or leave fullscreen; when it says the view may still be moving, take a new screenshot and try again, but when it says the window moved or resized again, aim at screenshot pixels instead; when it says the element’s centre is past its output’s edge, move the window so the element is on screen, or aim at a screenshot pixel; when it says another floating window lies over it, use activate_element or set_element_text, or aim at screenshot pixels on a fresh screenshot. For tiled, niri 26.04 gives the window no place to measure from: use activate_element or set_element_text, which don’t need a box, or aim at a screenshot pixel. Only when changing the user’s layout is fine, float the window with niri_action {"action": {"ToggleWindowFloating": {"id": <window_id>}}}, then take a new screenshot and call elements again before clicking. For outside_screenshot, take a screenshot that shows it. For action_unmapped, aim at the element’s pixels on a fresh screenshot.

Cause: nothing on the accessibility bus binds the element to the record it acts on, so the server can’t tell a list’s row reused for another record from the row it listed. The element sits in a list row with no name (unnamed_row), as GTK 4’s list items have unless the app labels them, or in no row while the walk read other objects of its role and name (repeated), as in most of a Qt Quick list. Those objects needn’t be listed: the extra match that tells elements its limit was reached counts too. elements marks such an element unbound. Nothing was sent.

What to do: don’t list again, which gives the same answer: take a screenshot and aim click at the element’s pixels in the row you mean.

Cause: activate_element or set_element_text was aimed at a field the toolkit marks as a password field: an element whose role is password_text, as GTK 4’s password entry and its entries for a password or PIN, GTK 3’s entries that hide their text and Qt’s password fields and fields that hide their text (NoEcho) are. The role is read again right before acting, so a field that became one is refused too. Nothing was sent.

What to do: have the user type it. A field that only hides its text may not be marked: a GTK 4 entry that hides its text without a password purpose is a plain text, with the same states, interfaces and attributes as an entry that shows its text, and isn’t refused, so don’t fill or activate a field that may hold a secret.

Cause: activate_element or set_element_text checks everything again right before its call, and every answer it relies on must be at most 250 ms old when the call goes out. The app, niri or Noctalia answered so slowly that the checks took longer than that twice in a row. Nothing was sent.

What to do: the app or the desktop is busy. Take a screenshot to see what it is doing, then try once more; if it stays busy, use the pointer or the keyboard, or ask the user.

Cause: niri_action was given a gated action, such as Spawn, Quit or LoadConfigFile, and the user’s policy doesn’t turn on unrestricted. The detail names the action and why it is gated. Nothing was sent.

What to do: tell the user the action needs unrestricted = true. Don’t reach the same result another way.

These mean niri, a program or Noctalia didn’t answer as expected. Report the name and the detail; repeating the same call in a loop won’t help.

Cause: NIRI_SOCKET isn’t set and the server found no running niri of yours, or found several; or niri’s socket is missing, refused the connection or closed it.

What to do: status shows the reason under niri.error. With several niri sessions, start the agent from inside the one you want or pass NIRI_SOCKET on (see Session variables).

Cause: niri, a program or an app didn’t answer in time: two seconds for niri and for each step of a clipboard read, five for grim, one for each call to an app’s accessibility interface and three for an elements walk as a whole, and for native keys 50 ms a key, at least three seconds a call.

What to do: check whether niri or the app is stuck. A stopped app makes elements time out; a large tree that is only slow gives a partial listing with capped_reason: "budget_exhausted" instead.

Cause: niri, a program, Noctalia or an app on the accessibility bus answered with an error or with something unreadable; the detail keeps its message, exit status and stderr. For activate_element and set_element_text, also when the app refused the action or the text; their detail names the call and the D-Bus error’s name, or application_error for a name the app made up, but never the message that came with it, which the app writes and could hold a field’s text. A pointer aimed at an element reports its errors the same way. Also when the server’s runtime directory was removed while it ran, which cancels a running action.

What to do: read the detail. After a removed runtime directory, restart the agent’s session.

Cause: Noctalia is installed but didn’t answer on its socket within two seconds, usually because it isn’t running.

What to do: start Noctalia, or ignore the shell tools. Without Noctalia running, the lock state relies on logind alone.

These come only from a server in shared mode, which relays its client to one engine per niri instance.

Cause: the shared engine ended, killed or crashed, or stopped reading from this server for five seconds, while the call was in flight, or since the session’s last call. What a call in flight did is unknown, and the session’s lease, refs and focus record went with the engine. The detail names the engine’s PID when the server knew it.

What to do: look at desktop_state or a screenshot before acting again, and call acquire_desktop again if you still need it. The next call reaches a new engine; the engine’s own error, if any, is in engine.log in the runtime directory.

Cause: after an engine was lost, the server couldn’t reach or start a new one within five seconds, or the new one refused it. The detail says why. The next call tries again.

What to do: read the detail and engine.log in the runtime directory. A server that can’t reach an engine when it starts serves its client on its own instead, so this happens only after a loss.