paperclipai/paperclip · error · Error

Failed to integrate concurrent remote git history for ${inpu

Error message

Failed to integrate concurrent remote git history for ${input.importedHead.slice(0, 12)} after multiple retries.

What it means

Thrown by integrateImportedGitHead after all 5 retry attempts fail with concurrent ref update lock errors. Each attempt calls `git update-ref` which fails with 'cannot lock ref ... expected ...', indicating another process is concurrently updating the same git ref. After exhausting retries, the integration gives up.

Source

Thrown at packages/adapter-utils/src/git-workspace-sync.ts:464

      ],
      {
        timeout: 60_000,
        maxBuffer: 64 * 1024,
      },
    );
    try {
      await runLocalGit(input.localDir, ["update-ref", headRef, mergeCommit.stdout.trim(), currentHead], {
        timeout: 10_000,
        maxBuffer: 16 * 1024,
      });
      return;
    } catch (error) {
      if (isConcurrentRefUpdateError(error) && attempt < 4) continue;
      throw error;
    }
  }

  throw new Error(`Failed to integrate concurrent remote git history for ${input.importedHead.slice(0, 12)} after multiple retries.`);
}

export async function resetLocalGitIndexToHead(input: {
  localDir: string;
  checkWorkingTreeClean?: boolean;
}): Promise<void> {
  try {
    await runLocalGit(input.localDir, ["reset", "--quiet", "HEAD", "--", "."], {
      timeout: 60_000,
      maxBuffer: 1024 * 1024,
    });
  } catch (error) {
    const detail = error && typeof error === "object"
      ? [
        (error as { message?: unknown }).message,
        (error as { stderr?: unknown }).stderr,
        (error as { stdout?: unknown }).stdout,
      ].filter((value): value is string => typeof value === "string" && value.trim().length > 0).join("\n")

View on GitHub (pinned to 67001ec6eb)

Solutions

  1. Serialize workspace sync operations so only one integrateImportedGitHead runs at a time per workspace.
  2. Remove stale git lock files (`.git/*.lock`, `.git/refs/**/*.lock`) if a previous process crashed.
  3. Check for concurrent agent runs targeting the same localDir and coordinate them.
  4. If on NFS/shared storage, move the workspace to a local filesystem to reduce lock contention.
Defensive patterns

Strategy: retry

Validate before calling

// Check for stale lock files before integrating
async function cleanStaleGitLocks(localDir: string): Promise<void> {
  const glob = await import("node:fs/promises");
  // Remove stale ref lock files older than 5 minutes
  // (only safe when no other git process is running)
}

Try / catch

try {
  await integrateImportedGitHead({ localDir, importedHead });
} catch (err) {
  if (err instanceof Error && err.message.includes("after multiple retries")) {
    // Lock contention persisted — serialize and retry after a delay
    await cleanStaleGitLocks(localDir);
    await integrateImportedGitHead({ localDir, importedHead });
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: Calling integrateImportedGitHead in a context where another concurrent process (another agent run, a parallel workspace sync, or a git gc) is locking the same ref. All 5 iterations of the for-loop hit isConcurrentRefUpdateError(error) and attempt < 4 continues, then the loop exits and this terminal throw fires.

Common situations: Multiple agent runs syncing to the same workspace simultaneously; a long-running git gc or repack holding ref locks; stale .git/refs/<ref>.lock files left by a crashed process; NFS or shared-filesystem lock contention.

Related errors


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