{"record":{"id":"a00e7a244e192657","repo":"paperclipai/paperclip","slug":"export-output-path-root-exists-and-is-not-a-dir","errorCode":null,"errorMessage":"Export output path ${root} exists and is not a directory.","messagePattern":"Export output path (.+?) exists and is not a directory\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"cli/src/commands/client/company.ts","lineNumber":1243,"sourceCode":"export function resolveExportOutputPath(root: string, relativePath: string): string {\n  const resolvedRoot = path.resolve(root);\n  const filePath = path.resolve(resolvedRoot, relativePath);\n  const rootPrefix = resolvedRoot.endsWith(path.sep) ? resolvedRoot : `${resolvedRoot}${path.sep}`;\n  if (filePath !== resolvedRoot && !filePath.startsWith(rootPrefix)) {\n    throw new Error(`Refusing to write export file outside output directory: ${relativePath}`);\n  }\n  return filePath;\n}\n\nexport async function confirmOverwriteExportDirectory(\n  outDir: string,\n  opts: { force?: boolean } = {},\n): Promise<void> {\n  const root = path.resolve(outDir);\n  const stats = await stat(root).catch(() => null);\n  if (!stats) return;\n  if (!stats.isDirectory()) {\n    throw new Error(`Export output path ${root} exists and is not a directory.`);\n  }\n\n  const entries = await readdir(root);\n  if (entries.length === 0) return;\n\n  // --force skips the guard for non-interactive/automated callers (e.g. the\n  // nightly backup routine, which exports into a git clone that legitimately\n  // still holds .git and BACKUP-README.md after cleaning tracked content).\n  if (opts.force) return;\n\n  if (!process.stdin.isTTY || !process.stdout.isTTY) {\n    throw new Error(`Export output directory ${root} already contains files. Re-run interactively, pass --force, or choose an empty directory.`);\n  }\n\n  const confirmed = await p.confirm({\n    message: `Overwrite existing files in ${root}?`,\n    initialValue: false,\n  });","sourceCodeStart":1225,"sourceCodeEnd":1261,"githubUrl":"https://github.com/paperclipai/paperclip/blob/67001ec6eb96ae601aa27bc91d9b2415d665334a/cli/src/commands/client/company.ts#L1225-L1261","documentation":"Thrown by confirmOverwriteExportDirectory when the resolved output path already exists as a file (or any non-directory entry) rather than a directory. The exporter needs to mkdir and write files under the root; a file blocking that path is a hard stop.","triggerScenarios":"Passing --out pointing at an existing regular file (e.g. a previous export zip, a README, a socket). Pointing --out at a path where a symlink resolves to a file. Reusing a name that was a file output from another tool.","commonSituations":"User previously ran 'paperclipai company export --out backup.zip' (file) then tries '--out backup.zip' for a folder export. A symlink in the path resolves to a file. Conflicting output naming conventions between scripts.","solutions":["Choose a different --out path that is or will be a directory.","Remove or rename the blocking file: 'rm <path>' (after confirming it is safe to delete).","If the path is a symlink to a file, repoint or remove the symlink.","Use a fresh directory name like 'export-<timestamp>'."],"exampleFix":"# before\npaperclipai company export --company cmp_abc --out backup.zip   # backup.zip is a file\n# after\npaperclipai company export --company cmp_abc --out backup-dir/","handlingStrategy":"validation","validationCode":"async function ensureExportOutIsDir(outDir: string): Promise<void> {\n  const fs = await import(\"fs/promises\");\n  const stat = await fs.stat(outDir).catch(() => null);\n  if (stat && !stat.isDirectory()) {\n    throw new Error(`Refusing export: ${outDir} is a file. Choose a directory path.`);\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use directory-style output names (trailing slash convention) in scripts.","Avoid reusing filenames previously used for zip outputs.","Check the path with 'ls -ld' before scheduling exports."],"tags":["cli","filesystem","company-export","validation"],"backgroundTag":null,"analyzedSha":"67001ec6eb96ae601aa27bc91d9b2415d665334a","analyzedAt":"2026-08-12T12:05:45.408Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}