paperclipai/paperclip · error
Invalid CreateOS file mode.
Error message
Invalid CreateOS file mode.
What it means
Each mapping may carry an optional POSIX file `mode`. The plugin validates it is an integer within 0–0o777 (0–511 decimal) before executing the transfer. Non-integer values, negatives, or modes above 0777 (e.g. setuid/sticky bits included) are rejected with this error.
Solutions
- Supply mode as a plain integer between 0 and 0o777, e.g. 0o600 or 420.
- If you have a full lstat mode, mask it: mode & 0o777.
- Ensure the value is a number type, not a string, before calling syncFiles.
Example fix
// before const mode = fs.statSync(local).mode; // e.g. 33188 // after const mode = fs.statSync(local).mode & 0o777; // e.g. 420 (0o644)
Defensive patterns
Strategy: validation
Validate before calling
const isValidMode = (m: unknown) =>
Number.isInteger(m) && (m as number) >= 0 && (m as number) <= 0o777;
if (mapping.mode != null && !isValidMode(mapping.mode)) throw new Error("bad mode"); Type guard
const isPosixMode = (v: unknown): v is number => typeof v === "number" && Number.isInteger(v) && v >= 0 && v <= 0o777;
Prevention
- Mask full stat modes with & 0o777 before use
- Write modes as octal literals (0o600) to avoid decimal confusion
- Keep modes as numbers through JSON serialization
When it happens
Trigger: A mapping sets mode to a non-integer (e.g. "644" string), a negative number, NaN, or a value > 0o777 such as 0o100644 (lstat-style mode) or 420 (decimal misinterpretation of 0644).
Common situations: Passing fs.Stats.mode (which includes file-type bits) directly instead of just permission bits; storing modes as decimal strings in config; JSON round-trips turning numbers into strings.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Invalid CreateOS transfer timeout.
- 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…
- A safe, unique --revision is required
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/c9ced89d17777af8.
Report an issue: GitHub.
Appendix: source
Thrown at packages/plugins/sandbox-providers/createos/src/file-sync.ts:96
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()}`;
// Outbound temporary files are on the target filesystem for atomic rename.View on GitHub (pinned to 3f1d897a7c)