paperclipai/paperclip · critical · Error

Refusing to use unsafe install-store marker ${paths.markerPa

Error message

Refusing to use unsafe install-store marker ${paths.markerPath}.

What it means

Thrown by initializeInstallStore() when the managed-install marker file (.managed-install) exists but is not a regular file — it is a symlink (isSymbolicLink) or has multiple hard links (nlink > 1). The marker file must be a unique regular file owned by the current user because its contents authenticate the store as genuinely Paperclip-managed; a symlinked or hardlinked marker could be spoofed.

Source

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

  return {
    paperclipHome,
    cliRoot,
    installsRoot: path.join(cliRoot, "installs"),
    manifestPath: path.join(cliRoot, "install.json"),
    markerPath: path.join(cliRoot, ".managed-install"),
    lockPath: path.join(cliRoot, ".install.lock"),
    currentPath: path.join(cliRoot, "current"),
    shimPath: path.join(homeDir, ".local", "bin", "paperclipai"),
  };
}

export function initializeInstallStore(paths = resolveInstallStorePaths()): void {
  ensurePrivateDirectory(paths.cliRoot);
  ensurePrivateDirectory(paths.installsRoot);
  try {
    const markerStat = fs.lstatSync(paths.markerPath);
    if (!markerStat.isFile() || markerStat.isSymbolicLink() || markerStat.nlink > 1) {
      throw new Error(`Refusing to use unsafe install-store marker ${paths.markerPath}.`);
    }
    assertOwnedByCurrentUser(markerStat, paths.markerPath);
    if (fs.readFileSync(paths.markerPath, "utf8") !== MANAGED_STORE_MARKER) {
      throw new Error(`Refusing to use unrecognized install store ${paths.cliRoot}.`);
    }
  } catch (error) {
    if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
    try {
      fs.writeFileSync(paths.markerPath, MANAGED_STORE_MARKER, { mode: 0o600, flag: "wx" });
    } catch (writeError) {
      if (
        (writeError as NodeJS.ErrnoException).code !== "EEXIST" ||
        fs.readFileSync(paths.markerPath, "utf8") !== MANAGED_STORE_MARKER
      ) {
        throw writeError;
      }
    }
  }

View on GitHub (pinned to 67001ec6eb)

Solutions

  1. Remove the offending marker entry: 'rm ~/.paperclip/cli/.managed-install' and let initializeInstallStore recreate it as a fresh regular file with mode 0o600.
  2. Confirm no symlink or hardlink was intentionally placed — if unexpected, audit for tampering.
  3. Re-run 'paperclipai install' after cleanup so the marker is written correctly.

Example fix

$ ls -la ~/.paperclip/cli/.managed-install
lrwxrwxrwx  .managed-install -> /tmp/fake-marker
$ rm ~/.paperclip/cli/.managed-install
$ paperclipai install   # recreates marker as regular file
Defensive patterns

Strategy: validation

Validate before calling

import fs from "node:fs";
import { resolveInstallStorePaths, MANAGED_STORE_MARKER } from "./install-store.js";

function isSafeMarker(paths = resolveInstallStorePaths()): boolean {
  try {
    const st = fs.lstatSync(paths.markerPath);
    return st.isFile() && !st.isSymbolicLink() && st.nlink === 1;
  } catch { return false; }
}

Type guard

import fs from "node:fs";

function isRegularUnlinkedFile(p: string): boolean {
  const st = fs.lstatSync(p);
  return st.isFile() && !st.isSymbolicLink() && st.nlink === 1;
}

Try / catch

try {
  initializeInstallStore(paths);
} catch (err) {
  if (err instanceof Error && err.message.includes("unsafe install-store marker")) {
    fs.rmSync(paths.markerPath, { force: true });
    initializeInstallStore(paths); // recreate
  } else throw err;
}

Prevention

When it happens

Trigger: Called initializeInstallStore() (directly or via withInstallStoreLock) where paths.markerPath exists and lstatSync reports it as a symlink or as a file with nlink > 1.

Common situations: 1) An attacker or misconfigured tool replaced ~/.paperclip/cli/.managed-install with a symlink to a marker file elsewhere. 2) A hard link was created to the marker from another location. 3) A backup/sync tool (rsync with --links) recreated the marker as a symlink.

Related errors


AI-assisted analysis of paperclipai/paperclip@67001ec6eb (2026-08-12). Data as JSON: /api/errors/31d0952799f7555b. Report an issue: GitHub.