paperclipai/paperclip · error · Error

Refusing to replace existing non-managed command ${paths.shi

Error message

Refusing to replace existing non-managed command ${paths.shimPath}.

What it means

Thrown by assertManagedShimWritable when the existing shim file's contents do not match the managed shim format (checked by isManagedShimContents, which validates a 5-line structure: shebang, marker comment, set -eu, exec line, trailing newline). This prevents the installer from silently overwriting a user-installed binary, a different tool's executable, or a manually-edited shim that no longer matches the managed format.

Source

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

  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);
    if (!directoryStat.isDirectory() || directoryStat.isSymbolicLink()) {
      throw new Error(`Refusing to use unsafe shim directory ${directoryPath}.`);
    }
    assertOwnedByCurrentUser(directoryStat, directoryPath);
  }
  try {
    const stat = fs.lstatSync(paths.shimPath);
    if (!stat.isFile() || stat.isSymbolicLink()) {
      throw new Error(`Refusing to replace non-regular shim ${paths.shimPath}.`);
    }
    assertOwnedByCurrentUser(stat, paths.shimPath);
    if (stat.nlink > 1) throw new Error(`Refusing to replace multiply linked shim ${paths.shimPath}.`);
    const existing = fs.readFileSync(paths.shimPath, "utf8");
    if (!isManagedShimContents(existing)) {
      throw new Error(`Refusing to replace existing non-managed command ${paths.shimPath}.`);
    }
  } catch (error) {
    if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
  }
}

function shellQuote(value: string): string {
  return `'${value.replace(/'/g, `'"'"'`)}'`;
}

function isManagedShimContents(contents: string): boolean {
  const lines = contents.split("\n");
  return (
    lines.length === 5 &&
    lines[0] === "#!/bin/sh" &&
    lines[1] === `# ${MANAGED_SHIM_MARKER}` &&
    lines[2] === "set -eu" &&
    /^exec '(?:[^']|'"'"')+' '(?:[^']|'"'"')+' "\$@"$/.test(lines[3]) &&

View on GitHub (pinned to 67001ec6eb)

Solutions

  1. Inspect the existing shim: 'cat ~/.local/bin/paperclipai' to see what it contains.
  2. If it is not a paperclip-managed shim, back it up and remove it: 'mv ~/.local/bin/paperclipai ~/.local/bin/paperclipai.bak'.
  3. If it is an older paperclip shim format, remove it so the installer can write the current format.
  4. Re-run the install command to write a fresh managed shim.

Example fix

// before: ~/.local/bin/paperclipai is a user script
// cat ~/.local/bin/paperclipai -> #!/usr/bin/env node ... (custom script)

// after: back up and remove, then reinstall
// mv ~/.local/bin/paperclipai ~/.local/bin/paperclipai.bak
// re-run: paperclipai install
Defensive patterns

Strategy: validation

Validate before calling

import fs from 'node:fs';

const MANAGED_SHIM_MARKER = 'paperclipai managed install shim v1';

function isManagedShim(shimPath: string): boolean {
  try {
    const contents = fs.readFileSync(shimPath, 'utf8');
    const lines = contents.split('\n');
    return (
      lines.length === 5 &&
      lines[0] === '#!/bin/sh' &&
      lines[1] === `# ${MANAGED_SHIM_MARKER}` &&
      lines[2] === 'set -eu' &&
      /^exec '(?:[^']|'"'"')+' '(?:[^']|'"'"')+' "\$@"$/.test(lines[3]) &&
      lines[4] === ''
    );
  } catch {
    return false;
  }
}

// Call before writeManagedShim:
if (fs.existsSync(paths.shimPath) && !isManagedShim(paths.shimPath)) {
  console.warn('Existing shim is not managed by paperclipai; backing up before overwrite.');
  fs.renameSync(paths.shimPath, `${paths.shimPath}.user-backup`);
}

Try / catch

try {
  writeManagedShim(paths);
} catch (error) {
  if (error instanceof Error && error.message.includes('non-managed command')) {
    // The path has a non-paperclip binary/script; back it up and retry
    const backup = `${paths.shimPath}.${Date.now()}.bak`;
    fs.renameSync(paths.shimPath, backup);
    console.warn(`Backed up existing command to ${backup}`);
    writeManagedShim(paths); // retry
  } else {
    throw error;
  }
}

Prevention

When it happens

Trigger: Calling assertManagedShimWritable or writeManagedShim when ~/.local/bin/paperclipai exists as a regular file but its contents are not the recognizable 5-line managed shim (e.g., it's a compiled binary, a script from another tool, or a manually customized shim).

Common situations: A different version of paperclipai installed a shim with a different format. A user placed their own script named 'paperclipai' in ~/.local/bin. A package manager installed a different binary at the same path. The managed shim was manually edited, breaking the format check.

Related errors


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