paperclipai/paperclip · error · Error
sandbox runtime asset key collides with a reserved runtime a
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.
Source
Thrown at packages/adapter-utils/src/sandbox-managed-runtime.ts:436
// git-backed workspace, under `<runtimeRootDir>/git-workspace-upload.tar`. Each
// asset stages under `<runtimeRootDir>/<key>-upload.tar`, so an asset key equal to
// one of these stems resolves to the same remote archive path. Reserve the stems.
const RESERVED_RUNTIME_ASSET_KEYS = new Set(["workspace", "git-workspace"]);
// Reject an asset key before any path is built from it. An asset key becomes a
// remote directory (`<runtimeRootDir>/<key>`), a remote archive name
// (`<key>-upload.tar`), and a host temp file (`<key>.tar`). A path separator or
// `..` in the key escapes those roots. A reserved stem makes the asset archive
// share a path with the workspace archive; under concurrent sync the asset task
// and the workspace task then write or upload the same archive at the same time,
// which fails extraction nondeterministically or puts asset bytes in the
// workspace. Fail closed on both cases.
function assertRuntimeAssetKeyIsSafe(key: string): void {
if (key.length === 0 || key.includes("/") || key.includes("\\") || key.includes("..")) {
throw new Error(`sandbox runtime asset key is not a simple path segment: ${key}`);
}
if (RESERVED_RUNTIME_ASSET_KEYS.has(key)) {
throw new Error(`sandbox runtime asset key collides with a reserved runtime archive name: ${key}`);
}
}
export function parseSandboxRemoteExecutionSpec(value: unknown): SandboxRemoteExecutionSpec | null {
const parsed = asObject(value);
const transport = asString(parsed.transport).trim();
const provider = asString(parsed.provider).trim();
const sandboxId = asString(parsed.sandboxId).trim();
const remoteCwd = asString(parsed.remoteCwd).trim();
const timeoutMs = asNumber(parsed.timeoutMs);
if (
transport !== "sandbox" ||
provider.length === 0 ||
sandboxId.length === 0 ||
remoteCwd.length === 0 ||
!Number.isFinite(timeoutMs) ||
timeoutMs <= 0View on GitHub (pinned to a7e689b3c3)
Solutions
- Rename the key to any unreserved single-segment value, e.g. "workspace-assets" or "ws-overlay".
- Treat the reserved set {"workspace", "git-workspace"} as off-limits when generating keys programmatically (mirror RESERVED_RUNTIME_ASSET_KEYS).
- 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
- Treat {"workspace", "git-workspace"} as a blocklist when generating keys.
- Do not name assets after top-level runtime directories.
- Add a manifest lint step that flags reserved keys before submission.
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
- sandbox runtime asset key is not a simple path segment: ${ke
- ${prefix}: "sandboxTransport" must be one of ${ADAPTER_LOGIN
- Sandbox duplex channel needs a plugin id, a provider key, an
- [adapter-ui-loader] Failed to load UI parser for "${adapterT
- Invalid status '${String(rawStatus)}'. Must be one of: ${PLU
AI-assisted analysis of paperclipai/paperclip@a7e689b3c3 (2026-08-21).
Data as JSON: /api/errors/af6ebd8f52e678f0.
Report an issue: GitHub.