paperclipai/paperclip · error
Invalid CreateOS transfer timeout.
Error message
Invalid CreateOS transfer timeout.
What it means
postUploadCommands may specify an optional timeoutMs for a remote command. The plugin validates it is an integer between 1 and 86,400,000 ms (24 hours). Missing, non-integer, zero, negative, or excessively large values throw this error during pre-flight validation.
Solutions
- Set timeoutMs to a positive integer of milliseconds within 1..86_400_000, e.g. 30_000.
- Omit timeoutMs entirely to use the plugin's default signal handling instead of a sentinel value.
- Coerce and validate numeric config values (Number(), Number.isInteger) before constructing operations.
Example fix
// before
{ cmd: "./postprocess.sh", timeoutMs: "30s" }
// after
{ cmd: "./postprocess.sh", timeoutMs: 30_000 } Defensive patterns
Strategy: validation
Validate before calling
const isValidTimeout = (t: unknown) =>
Number.isInteger(t) && (t as number) >= 1 && (t as number) <= 86_400_000;
for (const c of op.postUploadCommands ?? []) {
if (c.timeoutMs != null && !isValidTimeout(c.timeoutMs)) throw new Error("bad timeoutMs");
} Type guard
const isTimeoutMs = (v: unknown): v is number => typeof v === "number" && Number.isInteger(v) && v >= 1 && v <= 86_400_000;
Prevention
- Use millisecond integers, never strings or second values
- Omit timeoutMs rather than passing Infinity or huge sentinels
- Coerce config-driven numbers with Number() and Number.isInteger checks
When it happens
Trigger: A post-upload command defines timeoutMs as 0, a float, a negative number, a string like "30000", or a value exceeding one day (e.g. Number.MAX_SAFE_INTEGER intending "no timeout").
Common situations: Using Infinity/very large sentinels to mean unlimited; config parsing yields strings; unit confusion (seconds written where milliseconds are expected, e.g. 30 instead of 30_000).
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.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- --agent-name cannot be empty.
- Invalid CreateOS command timeout.
- Invalid CreateOS file mode.
- invalid_response
- : "timeoutPolicy" must be one of .
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/cda5238318ba18b8.
Report an issue: GitHub.
Appendix: source
Thrown at packages/plugins/sandbox-providers/createos/src/file-sync.ts:101
};
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.
const parent = direction === "out" ? path.dirname(local) : os.tmpdir();
await fs.mkdir(parent, { recursive: true });
const temp = await fs.mkdtemp(path.join(parent, ".paperclip-createos-"));
const transferFile = path.join(temp, "data");
try {View on GitHub (pinned to 3f1d897a7c)