{"record":{"id":"fb8cb7898e68b78b","repo":"Hmbown/CodeWhale","slug":"clipboard-read-is-not-exposed-by-hdc-on-current-harmonyos","errorCode":null,"errorMessage":"clipboard read is not exposed by hdc on current HarmonyOS builds","messagePattern":"clipboard read is not exposed by hdc on current HarmonyOS builds","errorType":"exception","errorClass":"ExecError","httpStatus":null,"severity":"error","filePath":"crates/tui/plugins/computer-use/src/backends/harmonyos.mjs","lineNumber":269,"sourceCode":"    hold_key: async ({ text, duration }) => {\n      if (String(text).toLowerCase() !== \"click\") throw new ExecError('hold_key on harmony supports only hold_key({\"text\":\"click\"}) = longClick');\n      const d = Math.max(1, Math.min(5, Number(duration) || 1));\n      return uiInput([\"longClick\", \"300\", \"300\"]);\n    },\n    set_value: async ({ target, value }) => {\n      const b = await centerOf(target);\n      await uiInput([\"click\", String(b.cx), String(b.cy)]);\n      await new Promise((r) => setTimeout(r, 300));\n      await uiInput([\"inputText\", String(b.cx), String(b.cy), escDeviceText(String(value))]);\n      return { action_sent: true, strategy: \"uitest-element\" };\n    },\n    select_text: async () => { throw new ExecError(\"select_text is not exposed by uitest dumpLayout/uiInput on the harmony backend\"); },\n    perform_action: async ({ target, action }) => {\n      const b = await centerOf(target);\n      if (action === \"longClick\") return uiInput([\"longClick\", String(b.cx), String(b.cy)]);\n      return uiInput([\"click\", String(b.cx), String(b.cy)]);\n    },\n    read_clipboard: async () => { throw new ExecError(\"clipboard read is not exposed by hdc on current HarmonyOS builds\"); },\n    write_clipboard: async () => { throw new ExecError(\"clipboard write is not exposed by hdc on current HarmonyOS builds\"); },\n    cursor_position: async () => { throw new ExecError(\"cursor position does not exist on touch devices\"); },\n    recordingStart: async ({ intervalMs = 400 } = {}) => {\n      if (recording) throw new ExecError(`recording ${recording.id} already running`);\n      if (!(await have(\"ffmpeg\"))) throw new ExecError(\"ffmpeg is required on the host to mux harmony snapshot-series recordings\");\n      const id = crypto.randomBytes(4).toString(\"hex\");\n      const dir = fs.mkdtempSync(path.join(os.tmpdir(), `cu-rec-${id}-`));\n      const startedAt = new Date().toISOString();\n      const rec = recording = { id, dir, startedAt, intervalMs, seq: 0, controller: new AbortController() };\n      const tick = () => {\n        if (rec.stopped || rec.pending) return rec.pending;\n        rec.pending = (async () => {\n          const opts = { timeoutMs: 15_000, signal: rec.controller.signal };\n          const remote = `${DEVICE_TMP}-rec-${id}-${String(rec.seq).padStart(5, \"0\")}.jpeg`;\n          try {\n            await deviceOut([\"snapshot_display\", \"-f\", remote], opts);\n            await exec.pullFile(remote, path.join(dir, `f${String(rec.seq).padStart(5, \"0\")}.jpeg`), opts);\n            rec.seq++;","sourceCodeStart":251,"sourceCodeEnd":287,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/plugins/computer-use/src/backends/harmonyos.mjs#L251-L287","documentation":"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.","triggerScenarios":"Calling read_clipboard() on the harmonyos backend, on any HarmonyOS version whose hdc build lacks a clipboard command.","commonSituations":"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.","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"],"exampleFix":"// before\nconst clip = await backend.read_clipboard();\n// after\nif (backend.name === \"harmonyos\") skip(\"clipboard read unsupported on harmony\");\nconst clip = await backend.read_clipboard();","handlingStrategy":"fallback","validationCode":"if (backend.name === \"harmonyos\") {\n  // clipboard read unsupported: use app UI or hdc file transfer instead\n}","typeGuard":null,"tryCatchPattern":"try {\n  clip = await backend.read_clipboard();\n} catch (e) {\n  if (String(e.message).includes(\"clipboard read is not exposed by hdc\")) clip = await readViaUiDump(backend);\n  else throw e;\n}","preventionTips":["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"],"tags":["harmonyos","clipboard","platform-limitation"],"backgroundTag":"unsupported-platform","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T21:17:16.096Z"}