paperclipai/paperclip · error · Error
post-upload command cwd is not a confined absolute POSIX pat
Error message
post-upload command cwd is not a confined absolute POSIX path: ${raw} What it means
Thrown by assertPostUploadCommandsConfined (Security Condition C2) when a post-upload command's cwd is not an absolute POSIX path or contains a '..' segment. This is a fail-closed host-side validation that runs before any sync handoff (native or fallback). The cwd must be absolute because relative paths would resolve against the runtime's stable cwd, not the operation's target, and '..' segments could escape the intended confinement root.
Source
Thrown at packages/adapter-utils/src/command-managed-runtime.ts:187
/**
* Host-side confinement guard for a sync operation's post-upload command `cwd`
* (Security Condition C2). Runs BEFORE any handoff — native delegation OR the
* generic fallback — so an out-of-root `cwd` is rejected fail-closed before a
* provider ever sees it. `cwd` (when present) MUST be an absolute POSIX path with
* no `..` segment, confined to (equal to or under) one of the operation's own
* file-mapping target paths. Commands with no `cwd` are unconstrained here and
* default to the runtime's stable command cwd at exec time.
*/
export function assertPostUploadCommandsConfined(operations: readonly SandboxSyncOperation[]): void {
for (const operation of operations) {
const commands = operation.postUploadCommands ?? [];
if (commands.length === 0) continue;
const targetRoots = operation.files.map((mapping) => path.posix.normalize(mapping.targetPath));
for (const command of commands) {
if (command.cwd == null) continue;
const raw = command.cwd;
if (!path.posix.isAbsolute(raw) || raw.split("/").includes("..")) {
throw new Error(`post-upload command cwd is not a confined absolute POSIX path: ${raw}`);
}
const normalized = path.posix.normalize(raw);
const within = targetRoots.some(
(root) => normalized === root || normalized.startsWith(`${root}/`),
);
if (!within) {
throw new Error(`post-upload command cwd escapes the operation's target root: ${raw}`);
}
}
}
}
export function createCommandManagedRuntimeClient(input: {
runner: CommandManagedRuntimeRunner;
commandCwd: string;
timeoutMs: number;
shellCommand?: "bash" | "sh" | null;
}): SandboxManagedRuntimeClient {View on GitHub (pinned to 67001ec6eb)
Solutions
- Change the cwd to an absolute POSIX path with no '..' segments that matches or is under one of the operation's file-mapping targetPaths.
- If you intended a relative path, compute the absolute path from the targetPath on the host before constructing the operation.
- Remove the cwd property entirely if the command should run from the runtime's default command cwd (which defaults to '/').
- Validate all postUploadCommands cwd values against path.posix.isAbsolute and ensure no '..' segments.
Example fix
// before: relative cwd with '..'
const ops: SandboxSyncOperation[] = [{
files: [{ kind: "directory", sourcePath: "./app", targetPath: "/workspace/app" }],
postUploadCommands: [{ command: "make build", cwd: "../build" }],
}];
// after: absolute POSIX path confined under targetPath
const ops: SandboxSyncOperation[] = [{
files: [{ kind: "directory", sourcePath: "./app", targetPath: "/workspace/app" }],
postUploadCommands: [{ command: "make build", cwd: "/workspace/app" }],
}]; Defensive patterns
Strategy: validation
Validate before calling
import path from 'node:path';
function arePostUploadCommandsConfined(operations: readonly SandboxSyncOperation[]): boolean {
for (const op of operations) {
const commands = op.postUploadCommands ?? [];
if (commands.length === 0) continue;
const targetRoots = op.files.map((m) => path.posix.normalize(m.targetPath));
for (const cmd of commands) {
if (cmd.cwd == null) continue;
if (!path.posix.isAbsolute(cmd.cwd) || cmd.cwd.split('/').includes('..')) return false;
const normalized = path.posix.normalize(cmd.cwd);
const within = targetRoots.some((root) => normalized === root || normalized.startsWith(`${root}/`));
if (!within) return false;
}
}
return true;
}
// Call before client.syncIn(operations):
if (!arePostUploadCommandsConfined(operations)) {
throw new Error('Post-upload command cwd validation failed; fix paths before sync.');
} Type guard
function isConfinedPostUploadCommand(
command: { cwd?: string | null },
targetRoots: string[],
): boolean {
if (command.cwd == null) return true;
if (!path.posix.isAbsolute(command.cwd) || command.cwd.split('/').includes('..')) return false;
const normalized = path.posix.normalize(command.cwd);
return targetRoots.some((root) => normalized === root || normalized.startsWith(`${root}/`));
} Try / catch
try {
await client.syncIn(operations);
} catch (error) {
if (error instanceof Error && error.message.includes('not a confined absolute POSIX path')) {
// Fix the cwd to be absolute with no '..' segments
console.error('Post-upload cwd must be absolute POSIX with no .. segments:', error.message);
}
throw error;
} Prevention
- Always use absolute POSIX paths for postUploadCommands cwd—never relative paths.
- Ensure the cwd is equal to or a subdirectory of one of the operation's file-mapping targetPaths.
- Remove the cwd property entirely if the command should run from the runtime default cwd.
- Call assertPostUploadCommandsConfined explicitly in tests to validate operations before runtime.
When it happens
Trigger: Calling client.syncIn(operations) or assertPostUploadCommandsConfined(operations) directly, where any operation has a postUploadCommands entry with a cwd that is relative (e.g., './build'), contains '..' (e.g., '/workspace/../etc'), or is not a POSIX-style absolute path.
Common situations: Configuring post-upload commands with relative cwd values assuming they resolve relative to the target path. Using '..' in the cwd to reference sibling directories. Windows-style absolute paths (C:\...) on a POSIX sandbox. Accidental empty string or malformed path.
Related errors
- post-upload command cwd escapes the operation's target root:
- networkAllowlist[${index}] must use an exact hostname; wildc
- ${action} failed with exit code ${result.exitCode ?? "null"}
- Could not determine remote file size for ${remotePath}
- Remote file read was truncated for ${remotePath}: ${out.byte
AI-assisted analysis of paperclipai/paperclip@67001ec6eb (2026-08-12).
Data as JSON: /api/errors/50c9f1f02424a622.
Report an issue: GitHub.