paperclipai/paperclip · error · Error

Failed to merge concurrent SSH git histories for ${currentHe

Error message

Failed to merge concurrent SSH git histories for ${currentHead.slice(0, 12)} and ${input.importedHead.slice(0, 12)}: ${reason}

What it means

Thrown during SSH workspace restore when `git merge-tree --write-tree <currentHead> <importedHead>` fails. The local git repo has divergent histories (the remote SSH workspace and the imported snapshot both advanced) and git could not compute a merge tree — typically because of a real merge conflict or a corrupted object. The wrapped reason preserves git's own error text.

Source

Thrown at packages/adapter-utils/src/ssh.ts:965

          timeout: 10_000,
          maxBuffer: 16 * 1024,
        });
        return;
      } catch (error) {
        if (isConcurrentRefUpdateError(error) && attempt < 4) continue;
        throw error;
      }
    }

    let mergedTree;
    try {
      mergedTree = await runLocalGit(input.localDir, ["merge-tree", "--write-tree", currentHead, input.importedHead], {
        timeout: 60_000,
        maxBuffer: 256 * 1024,
      });
    } catch (error) {
      const reason = error instanceof Error ? error.message : String(error);
      throw new Error(
        `Failed to merge concurrent SSH git histories for ${currentHead.slice(0, 12)} and ${input.importedHead.slice(0, 12)}: ${reason}`,
      );
    }
    const mergedTreeId = mergedTree.stdout.trim().split("\n")[0]?.trim() ?? "";
    if (!mergedTreeId) {
      throw new Error("Failed to compute a merged git tree for SSH workspace restore.");
    }

    const mergeCommit = await runLocalGit(
      input.localDir,
      [
        "commit-tree",
        mergedTreeId,
        "-p",
        currentHead,
        "-p",
        input.importedHead,
        "-m",

View on GitHub (pinned to 67001ec6eb)

Solutions

  1. Inspect the wrapped `reason` in the message: a conflict-style output means real content conflicts that need manual resolution before re-running the restore.
  2. If reason mentions timeout/buffer, raise the merge-tree budget or reduce workspace size; re-run restore when the workspace is quieter.
  3. Run `git merge-tree --write-tree <currentHead> <importedHead>` manually in input.localDir to reproduce and resolve the conflict.
  4. If the histories should not have diverged, verify the importedHead was captured from a consistent snapshot and retry after resetting the workspace to a known-good head.

Example fix

// before: restore divergent SSH workspace
await restoreSshWorkspace({ localDir, importedHead, spec });
// after: resolve the conflict locally first
// cd <localDir> && git merge-tree --write-tree <currentHead> <importedHead>
// fix conflicts, then re-run restore with a rebased importedHead
Defensive patterns

Strategy: try-catch

Try / catch

try {
  await restoreSshWorkspace({ localDir, importedHead, spec });
} catch (err) {
  if (err.message.startsWith("Failed to merge concurrent SSH git histories")) {
    // log importedHead/currentHead, surface conflict for manual resolution,
    // do not retry blindly — the underlying merge-tree conflict persists.
  }
  throw err;
}

Prevention

When it happens

Trigger: mergeConcurrentSshGitHistories (ssh.ts) detects currentHead !== importedHead and neither is an ancestor of the other (true divergence), then runs git merge-tree --write-tree which exits non-zero. Triggers on conflicting edits to the same files in both the SSH workspace and the imported restore snapshot.

Common situations: Two agents editing the same workspace file from different lanes; a restore attempted against a workspace that moved forward with conflicting changes; large repo where merge-tree hit the 256KB maxBuffer or 60s timeout; corrupted .git objects from an interrupted sync.

Related errors


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