paperclipai/paperclip · error
CreateOS transfer requires an absolute host path.
Error message
CreateOS transfer requires an absolute host path.
What it means
For inbound transfers the mapping's sourcePath is on the local host, and for outbound transfers the targetPath is; the CreateOS plugin requires that host-side path to be absolute. The check runs during validation before any files move, throwing this error when a relative host path is supplied.
Solutions
- Resolve host paths to absolute before building the operation, e.g. path.resolve(cwd, relative).
- For direction "in", make sourcePath absolute; for direction "out", make targetPath absolute.
- Add an assertion in the calling code that fails early with the offending path for easier diagnosis.
Example fix
// before
syncFiles({ direction: "in", operations: [{ files: [{ kind: "file", sourcePath: "./out.json", targetPath: "/work/out.json" }] }] });
// after
import path from "node:path";
syncFiles({ direction: "in", operations: [{ files: [{ kind: "file", sourcePath: path.resolve("./out.json"), targetPath: "/work/out.json" }] }] }); Defensive patterns
Strategy: validation
Validate before calling
import path from "node:path";
const hostPath = direction === "in" ? mapping.sourcePath : mapping.targetPath;
if (!path.isAbsolute(hostPath)) throw new Error(`host path must be absolute: ${hostPath}`); Prevention
- Always pass host paths through path.resolve()
- Never build host paths from unanchored relative config values
- Assert absoluteness at the boundary where operations are constructed
When it happens
Trigger: syncFiles is called with a mapping where `path.isAbsolute(sourcePath)` fails for direction "in" or `path.isAbsolute(targetPath)` fails for direction "out" — e.g. paths built from relative cwd strings like "./data" or "data/file.txt".
Common situations: Caller constructs paths from user-relative working directories without path.resolve; config values hold relative paths; code ported from another provider that accepted relative paths.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- Invalid PAPERCLIP_PAGE_DEFAULT_PREFIX: use lowercase path…
- Outbound CreateOS transfers cannot run post-upload commands.
- : invalid asset path
- runner_provider_package_root_incompatible
- Source must be a real directory
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/948e14d007d92ff9.
Report an issue: GitHub.
Appendix: source
Thrown at packages/plugins/sandbox-providers/createos/src/file-sync.ts:95
method: "PUT", body: Readable.toWeb(source) as ReadableStream<Uint8Array>,
duplex: "half", headers: { "Content-Type": "application/octet-stream" }, signal,
};
const response = await client.request(`/sandboxes/${id}/files?path=${encodeURIComponent(remote)}`, init);
await response.body?.cancel();
} finally { source.destroy(); }
};
const download = async (remote: string, local: string, mode = 0o600) => {
const response = await client.request(`/sandboxes/${id}/files?path=${encodeURIComponent(remote)}`, { signal });
if (!response.body) throw new Error("CreateOS file download has no body.");
await pipeline(response.body, createWriteStream(local, { flags: "wx", mode }), { signal });
};
// Validate every mapping before beginning side effects. Host paths are
// orchestrator-authored and checked by its source/target-root guard.
for (const operation of params.operations) {
for (const mapping of operation.files) {
if (!["file", "directory"].includes(mapping.kind)) throw new Error("Unsupported CreateOS transfer kind.");
if (!path.isAbsolute(direction === "in" ? mapping.sourcePath : mapping.targetPath)) throw new Error("CreateOS transfer requires an absolute host path.");
if (mapping.mode != null && (!Number.isInteger(mapping.mode) || mapping.mode < 0 || mapping.mode > 0o777)) throw new Error("Invalid CreateOS file mode.");
assertRemotePath(direction === "in" ? mapping.targetPath : mapping.sourcePath);
}
for (const command of operation.postUploadCommands ?? []) {
assertRemotePath(command.cwd ?? ROOT);
if (command.timeoutMs != null && (!Number.isInteger(command.timeoutMs) || command.timeoutMs < 1 || command.timeoutMs > 86_400_000)) throw new Error("Invalid CreateOS transfer timeout.");
}
if (direction === "out" && operation.postUploadCommands?.length) throw new Error("Outbound CreateOS transfers cannot run post-upload commands.");
}
for (const operation of params.operations) {
let bytesTransferred = 0;
let filesTransferred = 0;
for (const mapping of operation.files) {
signal.throwIfAborted();
const local = direction === "in" ? mapping.sourcePath : mapping.targetPath;
const remote = direction === "in" ? mapping.targetPath : mapping.sourcePath;
const scratch = `/tmp/paperclip-createos-transfer-${randomUUID()}`;View on GitHub (pinned to 3f1d897a7c)