paperclipai/paperclip · error · Error

Worktree seed lock ${lockPath} belongs to exited process ${c

Error message

Worktree seed lock ${lockPath} belongs to exited process ${currentOwner.pid}. Verify that no seed is running, then remove the stale lock and retry.

What it means

Thrown by acquireWorktreeSeedLock when the lock file exists, parses to a valid owner, but the owning process pid is no longer alive (and not EPERM). This means a previous seed process died holding the lock; the guard refuses to silently steal it and asks the user to confirm no seed is running before removing it.

Source

Thrown at cli/src/commands/worktree.ts:1534

        const current = await fsPromises.readFile(lockPath, "utf8").catch(() => null);
        if (current && parseWorktreeSeedLockOwner(current)?.token === owner.token) {
          await fsPromises.rm(lockPath, { force: true });
        }
      };
    } catch (error) {
      if ((error as NodeJS.ErrnoException).code !== "EEXIST") throw error;
    }

    const [rawOwner, lockStat] = await Promise.all([
      fsPromises.readFile(lockPath, "utf8").catch(() => null),
      fsPromises.stat(lockPath).catch(() => null),
    ]);
    const currentOwner = rawOwner ? parseWorktreeSeedLockOwner(rawOwner) : null;
    const malformedLockIsStale = Boolean(
      lockStat && Date.now() - lockStat.mtimeMs >= WORKTREE_SEED_LOCK_MALFORMED_STALE_MS,
    );
    if (currentOwner && !processIsAlive(currentOwner.pid)) {
      throw new Error(
        `Worktree seed lock ${lockPath} belongs to exited process ${currentOwner.pid}. `
        + "Verify that no seed is running, then remove the stale lock and retry.",
      );
    }
    if (!currentOwner && malformedLockIsStale) {
      throw new Error(
        `Worktree seed lock ${lockPath} is stale or malformed. `
        + "Verify that no seed is running, then remove the stale lock and retry.",
      );
    }
    await new Promise((resolve) => setTimeout(resolve, WORKTREE_SEED_LOCK_POLL_MS));
  }
}

export async function ensureWorktreeSeeded(
  opts: WorktreeEnsureSeededOptions = {},
  dependencies: { seedDatabase?: SeedWorktreeDatabase } = {},
): Promise<EnsureWorktreeSeededResult> {

View on GitHub (pinned to 67001ec6eb)

Solutions

  1. Confirm no seed process is running (ps aux | grep paperclip).
  2. Remove the stale lock file at the path shown in the message.
  3. Retry the seed command.
  4. Investigate why the prior process died (OOM, disk full) to avoid recurrence.

Example fix

# before: stale lock from dead pid
# after
ps aux | grep paperclip   # confirm none running
rm <lockPath-from-error>
paperclipai worktree reseed ...
Defensive patterns

Strategy: validation

Validate before calling

function lockOwnerIsAlive(lockPath: string): boolean {
  const raw = fs.readFileSync(lockPath, 'utf8');
  const owner = parseWorktreeSeedLockOwner(raw);
  return owner ? processIsAlive(owner.pid) : false;
}

Try / catch

try {
  await acquireWorktreeSeedLock(lockPath);
} catch (e) {
  if (/belongs to exited process/.test((e as Error).message)) {
    if (!anySeedRunning()) fs.rmSync(lockPath, { force: true });
    // retry
  } else throw e;
}

Prevention

When it happens

Trigger: A prior `paperclipai worktree reseed`/seed process was killed (SIGKILL, OOM, terminal closed) mid-seed; the lock file remained; a new seed attempt detects the dead owner.

Common situations: Long seed killed by timeout/OOM; user Ctrl-C'd then immediately retried before the lock was released; process died on a different host sharing the filesystem; IDE/shell closed abruptly.

Related errors


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