Hmbown/CodeWhale · error · ExecError
clipboard write is not exposed by hdc on current HarmonyOS…
Error message
clipboard write is not exposed by hdc on current HarmonyOS builds
What it means
Writing the device clipboard is not possible through hdc's uitest interface on current HarmonyOS builds, symmetric with read_clipboard. The backend throws this ExecError unconditionally instead of attempting a non-existent command.
Solutions
- Deliver text directly into the field with set_value or the text/input action instead of paste
- If paste flow must be tested, type the clipboard content into another field of the app and use the app's own copy UI
- Gate clipboard-write steps by backend and skip them on harmonyos
Example fix
// before
await backend.write_clipboard("hello");
await backend.key({ text: "enter" }); // paste flow
// after
await backend.set_value({ target: "#field", value: "hello" }); Defensive patterns
Strategy: fallback
Validate before calling
if (backend.name === "harmonyos") {
// clipboard write unsupported: use set_value / inputText instead
} Try / catch
try {
await backend.write_clipboard(text);
} catch (e) {
if (String(e.message).includes("clipboard write is not exposed by hdc")) await backend.set_value({ target, value: text });
else throw e;
} Prevention
- Seed field content with set_value instead of clipboard+paste
- Skip clipboard-setup steps on harmonyos in shared test suites
- Check hdc/uitest release notes before re-enabling clipboard actions
When it happens
Trigger: Calling write_clipboard("text") on the harmonyos backend, typically to stage data before a paste action in the app under test.
Common situations: Setup steps that seed the clipboard for paste-into-field tests; scripts ported from Android backends where clipboard write exists; cross-platform automation frameworks sharing one clipboard-based helper.
Understand the failure class
Background: "unsupported platform" / "not supported on this platform" errors: what they mean and how to fix them — this error's family across 47 libraries.
Related errors
- clipboard read is not exposed by hdc on current HarmonyOS…
- aa start failed
- another terminal clipboard write is still queued
- clipboard action must be "read" or "write
- clipboard action "write" requires text
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/078107904c48abd0.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/plugins/computer-use/src/backends/harmonyos.mjs:270
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"); },
cursor_position: async () => { throw new ExecError("cursor position does not exist on touch devices"); },
recordingStart: async ({ intervalMs = 400 } = {}) => {
if (recording) throw new ExecError(`recording ${recording.id} already running`);
if (!(await have("ffmpeg"))) throw new ExecError("ffmpeg is required on the host to mux harmony snapshot-series recordings");
const id = crypto.randomBytes(4).toString("hex");
const dir = fs.mkdtempSync(path.join(os.tmpdir(), `cu-rec-${id}-`));
const startedAt = new Date().toISOString();
const rec = recording = { id, dir, startedAt, intervalMs, seq: 0, controller: new AbortController() };
const tick = () => {
if (rec.stopped || rec.pending) return rec.pending;
rec.pending = (async () => {
const opts = { timeoutMs: 15_000, signal: rec.controller.signal };
const remote = `${DEVICE_TMP}-rec-${id}-${String(rec.seq).padStart(5, "0")}.jpeg`;
try {
await deviceOut(["snapshot_display", "-f", remote], opts);
await exec.pullFile(remote, path.join(dir, `f${String(rec.seq).padStart(5, "0")}.jpeg`), opts);
rec.seq++;
} finally { await shell(["rm", "-f", remote], opts).catch(() => {}); }View on GitHub (pinned to 73e0f67d83)