paperclipai/paperclip · critical · Error

Refusing to modify path not owned by the current user: ${tar

Error message

Refusing to modify path not owned by the current user: ${targetPath}.

What it means

Thrown by assertOwnedByCurrentUser() when a file or directory's stat.uid does not equal process.getuid(). The install store only modifies entries it owns to prevent one user from writing into another user's store (privilege escalation / cross-user tampering). The check is skipped on platforms without process.getuid (e.g. Windows).

Source

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

  markerPath: string;
  lockPath: string;
  currentPath: string;
  shimPath: string;
};

function ensurePrivateDirectory(directoryPath: string): void {
  fs.mkdirSync(directoryPath, { recursive: true, mode: 0o700 });
  const stat = fs.lstatSync(directoryPath);
  if (!stat.isDirectory() || stat.isSymbolicLink()) {
    throw new Error(`Refusing to use non-directory install-store path ${directoryPath}.`);
  }
  fs.chmodSync(directoryPath, 0o700);
}

function assertOwnedByCurrentUser(stat: fs.Stats, targetPath: string): void {
  const getuid = process.getuid;
  if (typeof getuid === "function" && stat.uid !== getuid()) {
    throw new Error(`Refusing to modify path not owned by the current user: ${targetPath}.`);
  }
}

function writeFileAtomic(filePath: string, contents: string, mode: number): void {
  const temporaryPath = path.join(
    path.dirname(filePath),
    `.${path.basename(filePath)}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`,
  );
  try {
    fs.writeFileSync(temporaryPath, contents, { mode, flag: "wx" });
    fs.renameSync(temporaryPath, filePath);
  } finally {
    fs.rmSync(temporaryPath, { force: true });
  }
}

export function resolveInstallStorePaths(options: {
  paperclipHome?: string;

View on GitHub (pinned to 67001ec6eb)

Solutions

  1. Run the CLI as the same user that owns the files ('chown' the store to the current user, or run without sudo).
  2. Fix ownership: 'sudo chown -R $(id -u):$(id -g) ~/.paperclip/cli'.
  3. In containers, ensure the volume mount is chowned to the container user's uid at image/mount time.
  4. Avoid running managed installs as root when the store was created by a non-root user.

Example fix

// before: files owned by uid 1000, running as root (uid 0)
$ sudo paperclipai install   # throws

// after: chown store to current user, run as that user
$ sudo chown -R $(id -u):$(id -g) ~/.paperclip/cli
$ paperclipai install
Defensive patterns

Strategy: validation

Validate before calling

import fs from "node:fs";

function ownedByCurrentUser(p: string): boolean {
  const getuid = process.getuid;
  if (typeof getuid !== "function") return true; // platforms without uid
  try { return fs.lstatSync(p).uid === getuid(); } catch { return false; }
}

// Before install ops:
if (!ownedByCurrentUser(paths.cliRoot)) {
  console.error(`Run 'sudo chown -R $(id -u):$(id -g) ${paths.cliRoot}'`);
}

Type guard

import fs from "node:fs";

function isOwnedByCurrentUser(p: string): boolean {
  const getuid = process.getuid;
  if (typeof getuid !== "function") return true;
  try { return fs.statSync(p).uid === getuid(); } catch { return false; }
}

Try / catch

try {
  assertManagedInstallStore(paths);
} catch (err) {
  if (err instanceof Error && err.message.includes("not owned by the current user")) {
    console.error("Fix ownership with: sudo chown -R $(id -u):$(id -g) ~/.paperclip/cli");
    process.exit(1);
  }
  throw err;
}

Prevention

When it happens

Trigger: Any install-store operation that calls assertOwnedByCurrentUser on a path whose owning uid differs from the current process uid. This includes marker files, cliRoot, shim path, and shell rc files during addManagedPathBlock/removeManagedPathBlock.

Common situations: 1) Running the CLI under 'sudo' or as root after a prior install was done as a normal user (root's uid 0 != file owner uid). 2) Two distinct OS users sharing a HOME. 3) Files restored from a tarball that preserved a different uid. 4) A container where the mounted volume is owned by a host uid that differs from the container user.

Related errors


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