paperclipai/paperclip · error · Error

sandbox runtime asset key collides with a reserved runtime…

Error message

sandbox runtime asset key collides with a reserved runtime archive name: ${key}

What it means

Thrown by assertRuntimeAssetKeyIsSafe when a runtime asset key is exactly "workspace" or "git-workspace". Those stems are reserved: the asset archive would share its path with the workspace archive, and under concurrent sync the two tasks would write/upload the same file simultaneously, corrupting extraction or leaking asset bytes into the workspace, so the runtime fails closed.

Solutions

  1. Rename the key to any unreserved single-segment value, e.g. "workspace-assets" or "ws-overlay".
  2. Treat the reserved set {"workspace", "git-workspace"} as off-limits when generating keys programmatically (mirror RESERVED_RUNTIME_ASSET_KEYS).
  3. If you meant to sync the workspace itself, use the workspace sync path, not a runtime asset.

Example fix

// before
const assets = [{ key: "workspace", source: archivePath }];

// after
const assets = [{ key: "workspace-overlay", source: archivePath }];
Defensive patterns

Strategy: validation

Validate before calling

const RESERVED = new Set(["workspace", "git-workspace"]);
function isUsableRuntimeAssetKey(key: string): boolean {
  return isSafeRuntimeAssetKey(key) && !RESERVED.has(key);
}

Type guard

function isUnreservedAssetKey(key: string): boolean {
  return key !== "workspace" && key !== "git-workspace";
}

Try / catch

try {
  buildRuntimeAssets(assets);
} catch (error) {
  if (error instanceof Error && error.message.includes("reserved runtime archive name")) {
    assets = assets.map((a) => a.key === badKey ? { ...a, key: `${a.key}-assets` } : a);
  } else throw error;
}

Prevention

When it happens

Trigger: A SandboxRemoteExecutionSpec runtimeAssets entry uses key "workspace" or "git-workspace" (e.g. author intended to ship a prebuilt workspace); assertRuntimeAssetKeyIsSafe rejects it before sync at sandbox-managed-runtime.ts:774.

Common situations: Reusing a natural directory name as the asset key; converting an older manifest that happened to name its asset "workspace"; tooling that defaults keys to the top-level folder name being archived.

Related errors


AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-08-21). Data as JSON: /api/errors/af6ebd8f52e678f0. Report an issue: GitHub.

Appendix: source

Thrown at packages/adapter-utils/src/sandbox-managed-runtime.ts:445

}

/**
 * A control command run against the sandbox after a sync operation's files have
 * landed. Mirrors the plugin SDK `PluginPostUploadCommand`; kept as a local
 * structural type so `adapter-utils` does not depend on the plugin SDK. Ordered
 * within {@link SandboxSyncOperation.postUploadCommands} and executed in array
 * order, fail-fast (first non-zero exit or timeout aborts the operation).
 *
 * SECURITY — command origin (Stage-1 design review, condition C1). `command` is
 * a **Paperclip/adapter-authored control operation**: it may be supplied ONLY by
 * core/adapter code. No server route, issue/comment content, project/workspace
 * file content, provider-plugin callback, or arbitrary adapter config may supply
 * a raw `command` string; any path embedded in it MUST be built by adapter/core
 * helpers from already-confined paths and shell-quoted (C3). Providers treat the
 * command as **opaque** — execute or reject, never rewrite/concatenate/append.
 */
export interface SandboxPostUploadCommand {
  /** The opaque, adapter-authored shell command to run after upload. */
  command: string;
  /**
   * Working directory for the command. When present, MUST be an absolute POSIX
   * path confined under the operation's allowed sandbox target root (C2). When
   * absent, defaults to the runtime's stable command cwd — never a process
   * default cwd.
   */
  cwd?: string;
  /** Optional per-command timeout in milliseconds. */
  timeoutMs?: number;
}

/**
 * An ordered, opaque unit of work handed to the native sync transport. The
 * `operationId` is an opaque, non-sensitive token authored by the orchestrator
 * (never a caller/asset identifier that could leak intent); a provider MUST NOT
 * interpret it.
 */

View on GitHub (pinned to 3f1d897a7c)