paperclipai/paperclip · error

CreateOS transfer command failed.

Error message

CreateOS transfer command failed.

What it means

The CreateOS sandbox plugin's internal `run` helper executes a shell command inside the remote sandbox during file synchronization (e.g. mkdir/tar/mv steps). If the command exits non-zero or times out, the plugin discards the actual exit details and throws this generic message. It signals that a remote-side transfer step failed without telling you which command or why.

Solutions

  1. Retry the sync with the sandbox exec endpoint to run the failing command manually and inspect its real stderr/exit code, since this error hides those details.
  2. Increase the timeoutMs on the transfer operation if the failure correlates with large files.
  3. Verify the sandbox image provides all tools the transfer relies on (bash, tar) and that the remote target directory is writable.
  4. Check sandbox disk usage and quotas; clean stale files and retry.

Example fix

// before: opaque failure
await run(`tar -xzf ${archive} -C ${remoteDir}`, remoteDir, 5_000);
// after: pre-check target writability and raise the timeout
await run(`mkdir -p ${remoteDir} && test -w ${remoteDir}`, remoteDir, 10_000);
await run(`tar -xzf ${archive} -C ${remoteDir}`, remoteDir, 60_000);
Defensive patterns

Strategy: retry

Validate before calling

// pre-flight: ensure remote tools exist
await run(`command -v bash && command -v tar`, ROOT, 10_000);

Try / catch

try {
  await syncFiles(params);
} catch (err) {
  if (err.message === "CreateOS transfer command failed.") {
    // inspect remote state, then retry with longer timeoutMs
  } else throw err;
}

Prevention

When it happens

Trigger: Any bash command run by syncFiles inside the sandbox (e.g. `mkdir -p`, `tar -x`, `mv`, `chmod` for the mapping) returns a non-zero exit code, or exceeds the optional timeoutMs passed by the caller and is aborted via AbortSignal.timeout.

Common situations: Remote sandbox image lacks a tool the transfer step needs (tar, bash built-ins); target directory is read-only or owned by another user; disk quota exceeded on the sandbox; timeoutMs set too low for large uploads; remote path already exists in a conflicting state.

Related errors


AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18). Data as JSON: /api/errors/30899ce1b72e7507. Report an issue: GitHub.

Appendix: source

Thrown at packages/plugins/sandbox-providers/createos/src/file-sync.ts:71

function excluded(name: string, patterns: string[]): boolean {
  name = name.replace(/^\.\//, "").replace(/\/$/, "");
  return patterns.some((pattern) => [pattern, `${pattern}/**`, `**/${pattern}`, `**/${pattern}/**`]
    .some((glob) => path.matchesGlob(name, glob)));
}

export async function syncFiles(
  client: CreateosClient,
  params: PluginEnvironmentSyncInParams,
  direction: "in" | "out",
  signal: AbortSignal,
): Promise<PluginEnvironmentSyncResult> {
  const id = identifier(params.lease.providerLeaseId);
  const operations: PluginEnvironmentSyncResult["operations"] = [];
  const run = async (command: string, cwd = ROOT, timeoutMs?: number) => {
    assertRemotePath(cwd);
    const result = await execute(client, { ...params, command: "/bin/bash", args: ["-c", command], cwd },
      timeoutMs == null ? signal : AbortSignal.any([signal, AbortSignal.timeout(timeoutMs)]));
    if (result.timedOut || result.exitCode !== 0) throw new Error("CreateOS transfer command failed.");
  };
  const upload = async (local: string, remote: string) => {
    const source = createReadStream(local);
    try {
      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 });
  };

View on GitHub (pinned to 3f1d897a7c)