paperclipai/paperclip · error · Error

Worktree seed source diagnostics changed while waiting for…

Error message

Worktree seed source diagnostics changed while waiting for the seed lock.

What it means

ensureWorktreeSeeded reads the legacy pending marker before acquiring the seed lock; under the lock it re-reads it and verifies the sourceConfigPath is unchanged. The lock is a polled cross-process lock (WORKTREE_SEED_LOCK_POLL_MS = 50), and this check detects that another actor rewrote the legacy source diagnostics while this process waited — i.e. two seed processes with different sources are racing on one worktree.

Solutions

  1. Ensure a single seed source per worktree: stop the competing service/process, then retry the seed
  2. If the legacy pending marker is definitively stale (no other seed running), remove it and re-run ensureWorktreeSeeded
  3. Re-register the worktree with one canonical source before seeding again
Defensive patterns

Strategy: retry

Try / catch

for (let attempt = 0; attempt < 2; attempt++) {
  try {
    return await ensureWorktreeSeeded(opts);
  } catch (error) {
    if (!String(error).includes('source diagnostics changed')) throw error;
    await stopCompetingSeedActors();
  }
}

Prevention

When it happens

Trigger: Two concurrent seed invocations with different registered/explicit sources for the same worktree: the first holds the lock and mutates legacy diagnostics; the second acquires the lock afterward and sees a different sourceConfigPath than its pre-lock read.

Common situations: Two services or terminals seeding the same worktree from different sources; a re-registration pointing at a new source while an old seed was still queued; automation overlapping ensure-seeded runs.

Related errors


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-08-21). Data as JSON: /api/errors/1f695a1af42a5db1. Report an issue: GitHub.

Appendix: source

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

  // Resolve all authority-bearing paths before creating the lock. The manifest is
  // agent-writable diagnostic evidence and never selects the source. A stale source
  // diagnostic is replaced under the lock from this server/operator registration.
  let canonicalSource = registeredSeedSource;
  mkdirSync(path.dirname(markers.lock), { recursive: true });
  const releaseLock = await acquireWorktreeSeedLock(markers.lock);
  try {
    // These checks deliberately happen under the cross-process lock. A second
    // service process waits for the first seed transaction, then observes the
    // verified manifest instead of cloning the same database concurrently.
    let manifest = readWorktreeSeedManifest(configPath);
    if (manifest?.state === "verified") {
      return { seeded: false, reason: "verified_manifest" };
    }
    if (!manifest && existsSync(markers.pending)) {
      const currentLegacyPending = readLegacyWorktreeSeedPendingMarker(markers.pending);
      if (currentLegacyPending.sourceConfigPath !== legacyPending?.sourceConfigPath) {
        throw new Error("Worktree seed source diagnostics changed while waiting for the seed lock.");
      }
      markWorktreeSeedPending({
        configPath,
        sourceConfigPath: registeredSeedSource.configPath,
        targetInstanceId: targetPaths.instanceId,
        seedMode: "minimal",
        diagnosticMessage: "Re-derived seed source diagnostics from the registered canonical source.",
      });
      manifest = readWorktreeSeedManifest(configPath);
    }
    if (!manifest) {
      const legacyEvidence = await (
        dependencies.inspectLegacyDatabase ?? inspectLegacyWorktreeDatabase
      )(configPath);
      if (legacyEvidence) {
        markWorktreeSeedPending({
          configPath,
          sourceConfigPath: registeredSeedSource.configPath,

View on GitHub (pinned to 01ad858492)