paperclipai/paperclip · error

Refusing to prune unsafe install-store path

Error message

Refusing to prune unsafe install-store path ${sourceRoot}.

What it means

pruneInstallPayloads lstat'ed a source root under the installs store before recursive removal and found it is not a plain directory; the guard refuses unsafe rm targets.

Solutions

  1. Do not prune unsafe paths; verify ${sourceRoot} is inside the managed installs root.
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at cli/src/install-store.ts:337 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-08-18). Data as JSON: /api/errors/0ad5697ce751239e. Report an issue: GitHub.

Appendix: source

Thrown at cli/src/install-store.ts:337

    .slice(0, 2);

  return { schemaVersion: INSTALL_MANIFEST_VERSION, ...record, previous };
}

export function pruneInstallPayloads(
  manifest: InstallManifest,
  paths = resolveInstallStorePaths(),
): string[] {
  const retained = new Set(
    [manifest, ...manifest.previous].map((record) => path.resolve(record.payloadPath)),
  );
  const removed: string[] = [];
  for (const source of ["npm", "git"] as const) {
    const sourceRoot = path.join(paths.installsRoot, source);
    if (!fs.existsSync(sourceRoot)) continue;
    const sourceStat = fs.lstatSync(sourceRoot);
    if (!sourceStat.isDirectory() || sourceStat.isSymbolicLink()) {
      throw new Error(`Refusing to prune unsafe install-store path ${sourceRoot}.`);
    }
    for (const entry of fs.readdirSync(sourceRoot)) {
      if (entry.startsWith(".")) continue;
      const candidate = path.join(sourceRoot, entry);
      if (!retained.has(path.resolve(candidate))) {
        fs.rmSync(candidate, { recursive: true, force: true });
        removed.push(candidate);
      }
    }
  }
  return removed;
}

export function assertManagedShimWritable(paths = resolveInstallStorePaths()): void {
  const homeDir = path.dirname(path.dirname(path.dirname(paths.shimPath)));
  for (const directoryPath of [homeDir, path.join(homeDir, ".local"), path.dirname(paths.shimPath)]) {
    if (!fs.existsSync(directoryPath)) continue;
    const directoryStat = fs.lstatSync(directoryPath);

View on GitHub (pinned to 01ad858492)