Hmbown/CodeWhale · error · ExecError
clipboard read is not exposed by hdc on current HarmonyOS…
Error message
clipboard read is not exposed by hdc on current HarmonyOS builds
What it means
Reading the device clipboard is not possible through hdc's uitest interface on current HarmonyOS builds — there is no dumpLayout/uiInput command that returns clipboard contents. The backend throws this ExecError from read_clipboard to make the platform gap explicit.
Solutions
- Use an on-device side channel: have the app under test expose the value via UI (then dumpLayout/set_value round-trip) or via a test hook
- Use hdc file transfer (send/receive a file the app writes) instead of the clipboard
- Skip clipboard assertions on HarmonyOS and gate those steps by backend type
Example fix
// before
const clip = await backend.read_clipboard();
// after
if (backend.name === "harmonyos") skip("clipboard read unsupported on harmony");
const clip = await backend.read_clipboard(); Defensive patterns
Strategy: fallback
Validate before calling
if (backend.name === "harmonyos") {
// clipboard read unsupported: use app UI or hdc file transfer instead
} Try / catch
try {
clip = await backend.read_clipboard();
} catch (e) {
if (String(e.message).includes("clipboard read is not exposed by hdc")) clip = await readViaUiDump(backend);
else throw e;
} Prevention
- Never assume clipboard parity across backends
- Move clipboard assertions behind a capability check
- Use hdc file send/receive or app-provided test hooks for data transfer on HarmonyOS
When it happens
Trigger: Calling read_clipboard() on the harmonyos backend, on any HarmonyOS version whose hdc build lacks a clipboard command.
Common situations: Workflows that copy data in an app and read it back for verification; cross-backend test suites that assume clipboard parity; attempting clipboard-based data transfer between apps in automation.
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 write 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/fb8cb7898e68b78b.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/plugins/computer-use/src/backends/harmonyos.mjs:269
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"); },
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++;View on GitHub (pinned to 73e0f67d83)