Hmbown/CodeWhale · error · RuntimeError
element_read_only
Error message
element_read_only
What it means
set_value on the linux backend writes text via the AT-SPI EditableText interface when the target exposes it. If the element's state lacks STATE_EDITABLE — meaning the interface exists but the control rejects text modification — the script raises RuntimeError("element_read_only") instead of attempting a doomed write.
Solutions
- Target the actual editable input rather than the read-only display element
- Remove the read-only condition in the application under automation first
- Use clipboard/keyboard strategies (if exposed by the backend) instead of direct text-set
- Verify the target selector resolves to the intended widget, not a sibling label
Example fix
// before
await backend.set_value({ target: "#summary-view", value: "hi" }) // read-only
// after
await backend.set_value({ target: "#summary-input", value: "hi" }); Defensive patterns
Strategy: validation
Validate before calling
const state = await backend.get_state?.(target);
if (state && !state.editable) throw new Error(`target ${target} is read-only`); Type guard
const isEditable = (s) => s?.editable === true;
Try / catch
try { await backend.set_value({ target, value }); } catch (e) { if (String(e).includes("element_read_only")) { return useKeyboardFallback(target, value); } throw e; } Prevention
- Target input widgets, not their read-only labels or display panes
- Inspect roles/states during script development to confirm editability
- Treat read-only surfaces as output-only; write to their backing controls instead
When it happens
Trigger: Calling set_value on a read-only text view, a label, a static document region, or any widget whose AT-SPI state omits STATE_EDITABLE while it still implements queryEditableText-adjacent interfaces.
Common situations: Automating a results/log pane that looks like a text field but is read-only; trying to edit a locked form after submission; targeting a password or computed field rendered as non-editable.
Related errors
- element_disabled
- invalid_value
- value_rejected
- value_verification_failed
- application not found or name is ambiguous in the AT-SPI…
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/38da1add92e4629a.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/plugins/computer-use/src/backends/linux.mjs:703
await xdotool(["keydown", k]);
await wait(d * 1000);
} finally { await releaseKey(k); }
} else await waylandKey(text, { holdMs: Math.round(d * 1000) });
return { action_sent: true, key: k, heldSec: d };
},
set_value: async ({ target, value }) => {
// Select a supported interface before sending input. A refused write or
// failed readback must never trigger a second, ambiguously applied edit.
const out = await atspiResolve(target, ` state = found.getState()
if not state.contains(pyatspi.STATE_ENABLED):
raise RuntimeError("element_disabled")
try:
editor = found.queryEditableText()
except NotImplementedError:
editor = None
if editor is not None:
if not state.contains(pyatspi.STATE_EDITABLE):
raise RuntimeError("element_read_only")
if not editor.setTextContents(extra):
raise RuntimeError("value_rejected")
text = found.queryText()
after = text.getText(0, text.characterCount)
if after != extra:
raise RuntimeError("value_verification_failed")
else:
import math
desired = float(extra)
if not math.isfinite(desired):
raise RuntimeError("invalid_value")
numeric = found.queryValue()
numeric.currentValue = desired
after = numeric.currentValue
if after != desired:
raise RuntimeError("value_verification_failed")
print(json.dumps({"ok": True, "after": after}))`, String(value));
if (!out.ok) throw new ExecError(`set_value failed: ${out.code}`);View on GitHub (pinned to 73e0f67d83)