paperclipai/paperclip · error
Unsupported CreateOS transfer kind.
Error message
Unsupported CreateOS transfer kind.
What it means
syncFiles validates every file mapping's `kind` before performing any side effects. Only "file" and "directory" transfers are supported by the CreateOS provider; any other kind value fails fast with this error. It is a pre-flight guard so an unsupported transfer never partially executes.
Solutions
- Set mapping.kind to exactly "file" or "directory" for every mapping in the operation.
- Validate operation payloads against the PluginEnvironmentSyncResult/params schema before calling syncFiles.
- If you need symlink transfers, restructure them as file contents or add provider support — they are not supported today.
Example fix
// before
{ kind: "dir", sourcePath: "/host/app", targetPath: "/work/app" }
// after
{ kind: "directory", sourcePath: "/host/app", targetPath: "/work/app" } Defensive patterns
Strategy: validation
Validate before calling
const ok = ops.every(op => op.files.every(f => f.kind === "file" || f.kind === "directory"));
if (!ok) throw new Error("mapping.kind must be 'file' or 'directory'"); Type guard
const isTransferKind = (k: unknown): k is "file" | "directory" => k === "file" || k === "directory";
Prevention
- Validate operations against the shared schema before calling syncFiles
- Use string-literal types/consts instead of free-form strings for kind
- Beware case differences from serialized JSON payloads
When it happens
Trigger: An operation in params.operations contains a file mapping whose `kind` is not exactly "file" or "directory" (e.g. "symlink", a typo like "dir", or an undefined kind from a malformed operation object).
Common situations: Orchestrator or caller code constructs mappings by hand with wrong literal strings; a schema change upstream introduces a new transfer kind before the CreateOS adapter supports it; JSON payloads with case differences ("File").
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- CreateOS transfer requires an absolute host path.
- Outbound CreateOS transfers cannot run post-upload commands.
- A full lowercase source SHA is required.
- A reusable lease cannot be replaced and reacquired in the…
- A reusable lease handoff requires an execution workspace…
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/d59fdfe7c692065a.
Report an issue: GitHub.
Appendix: source
Thrown at packages/plugins/sandbox-providers/createos/src/file-sync.ts:94
const init: RequestInit & { duplex: "half" } = {
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;View on GitHub (pinned to 3f1d897a7c)