Hmbown/CodeWhale · error · ExecError
hdc file recv failed
Error message
hdc file recv failed: ${r.stderr.trim().slice(0, 300)} What it means
pullFile() on the HDC (HarmonyOS) transport copies a file from the device with `hdc file recv`; a nonzero exit throws this ExecError embedding trimmed stderr (max 300 chars). Before running, the remote path is stripped of a leading '/' and validated by safeRemotePath to block traversal/metacharacters, so this error is about the hdc transfer itself failing.
Solutions
- Verify device connectivity: hdc list targets shows the expected device
- Confirm the file exists on device: hdc shell ls <remotePath>
- Check the local destination directory exists and is writable
- Read embedded stderr for the specific hdc failure (path, permission, connection)
- Ensure the correct device target is selected in targetArgs if multiple devices are attached
Example fix
// before
await ex.pullFile("/data/log/app.log", "/tmp/app.log"); // file missing on device
// after
const ls = await run("hdc", [...targetArgs, "shell", "ls", "/data/log/app.log"]);
if (ls.code !== 0) throw new Error(`device file missing: /data/log/app.log`);
await ex.pullFile("/data/log/app.log", "/tmp/app.log"); Defensive patterns
Strategy: validation
Validate before calling
// confirm the device and file exist before pullFile
const targets = await run("hdc", ["list", "targets"]);
if (!targets.stdout.trim()) throw new Error("no hdc device connected");
const ls = await run("hdc", [...targetArgs, "shell", "ls", remotePath]);
if (ls.code !== 0) throw new Error(`device file missing: ${remotePath}`); Type guard
function isHdcRecvFailure(e) { return e instanceof ExecError && e.message.startsWith("hdc file recv failed"); } Try / catch
try {
const local = await ex.pullFile(remotePath, localPath);
} catch (e) {
if (e.message.startsWith("hdc file recv failed")) {
console.error("hdc recv failed:", e.message); // embedded stderr: path vs connection
// reconnect device or correct the device path, then retry
}
throw e;
} Prevention
- Run hdc list targets before device operations
- Validate the device path exists with hdc shell ls
- Ensure the local destination directory exists and is writable
- Select the correct target when multiple devices are attached
When it happens
Trigger: Calling executor.pullFile(remotePath, localPath) when hdc exits nonzero: device not connected, remote path does not exist on device, permission denied on device path, or localPath not writable.
Common situations: HarmonyOS device disconnected or unauthorized; typo'd device path (file absent); target selector (targetArgs) pointing at the wrong connected device; local destination directory missing; hdc daemon not running on the device.
Related errors
- hdc shell exited
- invalid_target
- aa start failed
- clipboard read is not exposed by hdc on current HarmonyOS…
- clipboard write is not exposed by hdc on current HarmonyOS…
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/1a6721c43dfc0279.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/plugins/computer-use/src/transport.mjs:310
return { installed: rels.length, remotePlatform: reply.platform, agentPath: `${marker}/agent.mjs` };
}
/** hdc (HarmonyOS) executor. Commands run on-device; files pull to local tmp. */
export function hdcExec(computer) {
const targetArgs = computer.target ? ["-t", computer.target] : [];
const shell = (args, opts = {}) => run("hdc", [...targetArgs, "shell", ...args], opts);
return {
kind: "hdc",
targetArgs,
run,
runOk,
shell,
async pullFile(remotePath, localPath, opts = {}) {
// HDC device captures use absolute paths; SSH agent paths are relative.
// Validate the remaining path with the same traversal/metacharacter guard.
safeRemotePath(typeof remotePath === "string" ? remotePath.replace(/^\//, "") : remotePath);
const r = await run("hdc", [...targetArgs, "file", "recv", remotePath, localPath], opts);
if (r.code !== 0) throw new ExecError(`hdc file recv failed: ${r.stderr.trim().slice(0, 300)}`, r);
return localPath;
},
async readFile(remotePath, opts = {}) {
// Containment: pull into a private mkdtemp dir and remove exactly that
// dir. Never rm() the parent of a file placed directly in os.tmpdir() —
// that recursively deletes the entire user temp directory.
const dir = await fs.promises.mkdtemp(path.join(os.tmpdir(), "cu-hdc-"));
try {
const tmp = path.join(dir, "out");
await this.pullFile(remotePath, tmp, opts);
return await fs.promises.readFile(tmp);
} finally {
// Cleanup must not replace downloaded bytes or the original I/O error.
await fs.promises.rm(dir, { recursive: true, force: true }).catch(() => {});
}
},
};
}View on GitHub (pinned to 73e0f67d83)