paperclipai/paperclip · error · Error
Refusing to write export file outside output directory
Error message
Refusing to write export file outside output directory: ${relativePath} What it means
resolveExportOutputPath is a path-traversal guard: after resolving root and the archive-relative entry path, the target no longer sits under the output root, so writing it would escape the export directory.
Solutions
- Fix the export package so all entries resolve inside the chosen output directory; do not hand-craft paths with '..' segments.
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at cli/src/commands/client/company.ts:1230 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of paperclipai/paperclip@120ae5428f (2026-08-18).
Data as JSON: /api/errors/d99711e7e82d0f9d.
Report an issue: GitHub.
Appendix: source
Thrown at cli/src/commands/client/company.ts:1230
for (const [relativePath, content] of Object.entries(exported.files)) {
const normalized = relativePath.replace(/\\/g, "/");
const filePath = resolveExportOutputPath(root, normalized);
await mkdir(path.dirname(filePath), { recursive: true });
const writeValue = portableFileEntryToWriteValue(content);
if (typeof writeValue === "string") {
await writeFile(filePath, writeValue, "utf8");
} else {
await writeFile(filePath, writeValue);
}
}
}
export function resolveExportOutputPath(root: string, relativePath: string): string {
const resolvedRoot = path.resolve(root);
const filePath = path.resolve(resolvedRoot, relativePath);
const rootPrefix = resolvedRoot.endsWith(path.sep) ? resolvedRoot : `${resolvedRoot}${path.sep}`;
if (filePath !== resolvedRoot && !filePath.startsWith(rootPrefix)) {
throw new Error(`Refusing to write export file outside output directory: ${relativePath}`);
}
return filePath;
}
export async function confirmOverwriteExportDirectory(
outDir: string,
opts: { force?: boolean } = {},
): Promise<void> {
const root = path.resolve(outDir);
const stats = await stat(root).catch(() => null);
if (!stats) return;
if (!stats.isDirectory()) {
throw new Error(`Export output path ${root} exists and is not a directory.`);
}
const entries = await readdir(root);
if (entries.length === 0) return;
View on GitHub (pinned to 120ae5428f)