paperclipai/paperclip · warning · Error

Another managed install is already running${ownerLabel}. If

Error message

Another managed install is already running${ownerLabel}. If no install process is active, remove the stale lock at ${paths.lockPath} and retry.

What it means

Thrown by withInstallStoreLock() when the install lock file (.install.lock) is held and the owning PID is still alive (process.kill(pid,0) succeeds or returns EPERM, meaning the process exists). The library auto-recovers stale locks (dead owner) but refuses to steal a lock from a live process. The message includes the owner PID when parseable and tells the user exactly which lock file to remove if they are sure no install is running.

Source

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

    const temporaryPath = `${paths.lockPath}.${token}.tmp`;
    try {
      fs.writeFileSync(temporaryPath, `${token}\n`, { mode: 0o600, flag: "wx" });
      try {
        fs.linkSync(temporaryPath, paths.lockPath);
        return;
      } catch (error) {
        if ((error as NodeJS.ErrnoException).code !== "EEXIST") throw error;
      }
      const owner = fs.readFileSync(paths.lockPath, "utf8").trim();
      const ownerPid = Number.parseInt(owner.split(":", 1)[0] ?? "", 10);
      if (Number.isInteger(ownerPid) && ownerPid > 0 && !processIsAlive(ownerPid)) {
        fs.rmSync(paths.lockPath);
        fs.rmSync(temporaryPath, { force: true });
        acquire();
        return;
      }
      const ownerLabel = Number.isInteger(ownerPid) && ownerPid > 0 ? ` (pid ${ownerPid})` : "";
      throw new Error(
        `Another managed install is already running${ownerLabel}. ` +
        `If no install process is active, remove the stale lock at ${paths.lockPath} and retry.`,
      );
    } finally {
      fs.rmSync(temporaryPath, { force: true });
    }
  };

  acquire();
  try {
    return await callback();
  } finally {
    try {
      if (fs.readFileSync(paths.lockPath, "utf8").trim() === token) {
        fs.rmSync(paths.lockPath, { force: true });
      }
    } catch (error) {
      if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;

View on GitHub (pinned to 67001ec6eb)

Solutions

  1. Wait for the other install (pid shown in the message) to finish, then retry.
  2. Confirm via 'ps -p <pid>' whether the owner is genuinely an install process; if it is hung, kill it ('kill <pid>').
  3. If no install is actually running (lock is stale but PID was reused by an unrelated live process), remove the lock file at the path shown and retry.
  4. Serialize install invocations with a higher-level lock or queue to avoid concurrent runs.

Example fix

$ paperclipai install
Error: Another managed install is already running (pid 12345)...
$ ps -p 12345   # confirm if real install
$ kill 12345    # if hung
$ rm ~/.paperclip/cli/.install.lock
$ paperclipai install
Defensive patterns

Strategy: retry

Validate before calling

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

function isLockHeldByLiveProcess(paths = resolveInstallStorePaths()): boolean {
  try {
    const owner = fs.readFileSync(paths.lockPath, "utf8").trim();
    const pid = Number.parseInt(owner.split(":", 1)[0] ?? "", 10);
    if (!Number.isInteger(pid) || pid <= 0) return true; // unknown owner → treat as held
    try { process.kill(pid, 0); return true; }
    catch (e) { return (e as NodeJS.ErrnoException).code === "EPERM"; }
  } catch { return false; }
}

Try / catch

try {
  return await withInstallStoreLock(doInstall, paths);
} catch (err) {
  if (err instanceof Error && err.message.startsWith("Another managed install is already running")) {
    // Option A: wait and retry. Option B: surface to user with the PID + lock path.
    console.error(err.message);
    process.exit(1);
  }
  throw err;
}

Prevention

When it happens

Trigger: Called withInstallStoreLock() while another process holds the lock: linkSync(temporaryPath, lockPath) returned EEXIST, the owner PID read from the lock is a positive integer, and processIsAlive(ownerPid) returned true (process exists).

Common situations: 1) Two concurrent 'paperclipai install' invocations. 2) A previous install is still running in another terminal or CI step. 3) A long-running install hung without exiting. 4) A background scheduler kicked off a second install before the first finished.

Related errors


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