Hmbown/CodeWhale · error · ExecError
hold_key on harmony supports only hold_key
Error message
hold_key on harmony supports only hold_key({"text":"click"}) = longClick What it means
hold_key on the harmony backend is implemented as a single uiInput longClick at fixed coordinates (300,300) and therefore only accepts text === "click". Any other text throws this ExecError. The duration parameter is clamped 1-5 but the harmony uitest path does not actually vary hold length per key, so non-click holds are unsupported.
Solutions
- Use hold_key({text:"click"}) for long-press, and target the element first if 300,300 is not the right spot
- For keyboard keys use key() instead of hold_key
- Use perform_action({target, action:"longClick"}) to long-press a located element at its center
Example fix
// before
await backend.hold_key({ text: "enter", duration: 2 });
// after
await backend.perform_action({ target: "#send", action: "longClick" }); Defensive patterns
Strategy: validation
Validate before calling
if (action === "hold_key" && String(opts.text).toLowerCase() !== "click") {
throw new Error("harmony backend: hold_key supports only text=click (longClick)");
} Type guard
const isHarmonyHoldKey = (opts) => opts && typeof opts.text === "string" && opts.text.toLowerCase() === "click";
Try / catch
try {
await backend.hold_key({ text: "click" });
} catch (e) {
if (String(e.message).includes("hold_key on harmony")) await backend.perform_action({ target, action: "longClick" });
else throw e;
} Prevention
- Only call hold_key with text "click" on harmony
- Prefer perform_action({target, action:"longClick"}) for element long-presses
- Do not expect duration to control hold length on this backend
When it happens
Trigger: Calling hold_key({text:"enter"}) or hold_key({text:"a", duration:3}) on the harmonyos backend — anything except text "click" (case-insensitive).
Common situations: Reusing desktop/mobile automation scripts that long-press keyboard keys; assuming hold_key generalizes key like on other backends; trying to long-press a specific element without realizing coordinates are hardcoded to 300,300.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- aa start failed
- active provider has no native web-search adapter
- atomic built-in snapshot publication is unsupported on this…
- Browser login is only enabled for the canonical Codewhale…
- browser opening is unsupported on this platform
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/e6479b40142fd26d.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/plugins/computer-use/src/backends/harmonyos.mjs:252
const dy = direction === "up" ? dist : direction === "down" ? -dist : 0;
return uiInput(["swipe", String(Math.round(target.x)), String(Math.round(target.y)), String(Math.round(target.x + dx)), String(Math.round(target.y + dy)), "400"]);
},
type: async ({ text }) => {
if (!text) return { action_sent: false, note: "empty text" };
await uiInput(["inputText", "300", "300", escDeviceText(text)]).catch(async (e) => {
// Some builds require coordinates of the focused field; retry with a click-first pattern.
throw e;
});
return { action_sent: true, chars: text.length, note: "inputText at 300,300 — click the field first for focused input" };
},
key: ({ text }) => {
const KEYMAP = { enter: "Enter", return: "Enter", escape: "Esc", esc: "Esc", back: "Back", home: "Home", backspace: "Back", delete: "Del", tab: "Tab", left: "DPAD_LEFT", right: "DPAD_RIGHT", up: "DPAD_UP", down: "DPAD_DOWN", power: "Power", menu: "Menu" };
const k = KEYMAP[String(text).toLowerCase()] ?? String(text);
if (!/^[A-Za-z0-9_]+$/.test(k)) throw new ExecError(`unsupported key "${text}" on harmony backend`);
return uiInput(["keyEvent", k]);
},
hold_key: async ({ text, duration }) => {
if (String(text).toLowerCase() !== "click") throw new ExecError('hold_key on harmony supports only hold_key({"text":"click"}) = longClick');
const d = Math.max(1, Math.min(5, Number(duration) || 1));
return uiInput(["longClick", "300", "300"]);
},
set_value: async ({ target, value }) => {
const b = await centerOf(target);
await uiInput(["click", String(b.cx), String(b.cy)]);
await new Promise((r) => setTimeout(r, 300));
await uiInput(["inputText", String(b.cx), String(b.cy), escDeviceText(String(value))]);
return { action_sent: true, strategy: "uitest-element" };
},
select_text: async () => { throw new ExecError("select_text is not exposed by uitest dumpLayout/uiInput on the harmony backend"); },
perform_action: async ({ target, action }) => {
const b = await centerOf(target);
if (action === "longClick") return uiInput(["longClick", String(b.cx), String(b.cy)]);
return uiInput(["click", String(b.cx), String(b.cy)]);
},
read_clipboard: async () => { throw new ExecError("clipboard read is not exposed by hdc on current HarmonyOS builds"); },
write_clipboard: async () => { throw new ExecError("clipboard write is not exposed by hdc on current HarmonyOS builds"); },View on GitHub (pinned to 73e0f67d83)