paperclipai/paperclip · error
Sandbox path alias "${aliasPath}" must target the synchroniz
Error message
Sandbox path alias "${aliasPath}" must target the synchronized workspace "${workspaceDir}". What it means
Thrown in the pathAliases loop of buildLocalProcessSandboxSpawnTarget when path.relative(workspaceDir, alias.target) escapes the workspace. Path aliases bind a host path (alias.target) to an in-sandbox path (alias.path) via bwrap --bind; only targets inside the synchronized workspace are allowed, because anything else would either duplicate content the sandbox cannot see or diverge from the synced tree.
Source
Thrown at packages/adapter-utils/src/local-process-sandbox.ts:418
addParentDirectories(args, created, normalized);
args.push(access === "rw" ? "--bind" : "--ro-bind", normalized, normalized);
mounted.add(normalized);
created.add(normalized);
};
for (const systemPath of SYSTEM_READ_PATHS) await mount(systemPath, "ro");
for (const executablePath of await executableReadPaths(input.executable)) await mount(executablePath, "ro");
if (networkScope === "allowlist") {
for (const nodePath of await executableReadPaths(process.execPath)) await mount(nodePath, "ro");
}
for (const managedPath of input.options.managedPaths ?? []) await mount(managedPath.path, managedPath.access);
for (const extraPath of input.options.extraPaths ?? []) await mount(extraPath.path, extraPath.access);
await mount(workspaceDir, "rw");
for (const [index, alias] of (input.options.pathAliases ?? []).entries()) {
const aliasPath = normalizeAbsolutePath(alias.path, `Sandbox pathAliases[${index}].path`);
const aliasTarget = normalizeAbsolutePath(alias.target, `Sandbox pathAliases[${index}].target`);
const relativeTarget = path.relative(workspaceDir, aliasTarget);
if (relativeTarget.startsWith("..") || path.isAbsolute(relativeTarget)) {
throw new Error(
`Sandbox path alias "${aliasPath}" must target the synchronized workspace "${workspaceDir}".`,
);
}
if (!(await pathExists(aliasTarget))) {
throw new Error(`Sandbox path alias target "${aliasTarget}" does not exist.`);
}
addParentDirectories(args, created, aliasPath);
args.push("--bind", aliasTarget, aliasPath);
created.add(aliasPath);
}
if (networkScope === "allowlist") {
const tempDir = await createNetworkProxyTempDir();
const socketPath = path.join(tempDir, "proxy.sock");
const bridgePath = path.join(tempDir, "bridge.cjs");
await fs.writeFile(bridgePath, await createNetworkProxyBridge(), { mode: 0o500 });
const proxy = await startNetworkAllowlistProxy(
input.options.networkAllowlist ?? [],View on GitHub (pinned to 67001ec6eb)
Solutions
- Move the alias target inside workspaceDir, or expand workspaceDir to cover the target's parent.
- If the target must live outside the workspace and is read-only, expose it via extraPaths with access "ro" instead of pathAliases (extraPaths is not bound to the workspace-only-target rule).
- Resolve symlinks on alias.target before sandbox spawn — an in-workspace symlink that resolves outside is the typical surprise.
- Re-check each alias.target against path.relative(workspaceDir, aliasTarget) in your config validator so the failure surfaces at load time.
Example fix
// before
pathAliases: [{ path: "/work/.env", target: "/home/user/.env" }]
// workspaceDir = "/srv/work"
// after: copy .env into the workspace before sandboxing
await fs.copyFile("/home/user/.env", "/srv/work/.env");
pathAliases: [{ path: "/work/.env", target: "/srv/work/.env" }] Defensive patterns
Strategy: validation
Validate before calling
function assertPathAliasesInWorkspace(workspaceDir: string, aliases: { path: string; target: string }[]): void {
for (const alias of aliases) {
const rel = path.relative(workspaceDir, alias.target);
if (rel.startsWith("..") || path.isAbsolute(rel)) {
throw new Error(`Alias ${alias.path} target ${alias.target} outside workspace ${workspaceDir}`);
}
}
}
const realWorkspace = await fs.realpath(workspaceDir);
const realAliases = await Promise.all(aliases.map(async (a) => ({ ...a, target: await fs.realpath(a.target) })));
assertPathAliasesInWorkspace(realWorkspace, realAliases); Try / catch
try {
return await buildLocalProcessSandboxSpawnTarget(input);
} catch (error) {
if (error instanceof Error && error.message.includes("must target the synchronized workspace")) {
throw new ConfigError(`Path alias target outside workspace. Move target inside ${workspaceDir} or use extraPaths with access ro.`, { cause: error });
}
throw error;
} Prevention
- Resolve alias.target symlinks before sandbox spawn.
- Prefer extraPaths with access "ro" for read-only out-of-workspace paths.
- Validate every alias target's relative path against the workspace at config load.
When it happens
Trigger: pathAliases: [{ path: "/work/node_modules", target: "/host/home/user/node_modules" }] with workspaceDir: "/srv/work". The check at local-process-sandbox.ts:417-421 fires for each alias whose target is outside the workspace (starts with ".." or is absolute-relative).
Common situations: Adapters that try to alias host home-directory dotfiles, system-wide caches, or symlinked dependencies that live outside the project. Also seen when workspaceDir is set to a subdirectory but aliases target files in the project root one level up.
Related errors
- Sandbox cwd "${cwd}" must be inside workspaceDir "${workspac
- Writable sandbox path "${normalizedExtraPath}" is outside sy
- filesystemScope must be "workspace".
- Sandbox path alias target "${aliasTarget}" does not exist.
- filesystemExtraPaths[${index}] must be an absolute path or {
AI-assisted analysis of paperclipai/paperclip@67001ec6eb (2026-08-12).
Data as JSON: /api/errors/b7bd28e972ec3dc7.
Report an issue: GitHub.