abhigyanpatwari/GitNexus · critical · RuntimeError

clone unexpectedly has an external object alternate: {altern

Error message

clone unexpectedly has an external object alternate: {alternates}

What it means

Thrown by make_worktree after a `git clone --no-local --no-hardlinks`. That clone mode is chosen so the benchmark arm gets a fully self-contained object database; if `.git/objects/info/alternates` exists afterward, the clone is secretly borrowing objects from another store, which breaks the isolation guarantee (the arm could read or depend on host state it should not see).

Source

Thrown at eval/workflow_bench/runner_artifacts.py:321

    target = Path(tempfile.mkdtemp(prefix="wfbench-", dir=parent))
    target.rmdir()
    try:
        run_checked(
            [
                "git",
                "clone",
                "--no-local",
                "--no-hardlinks",
                "--no-tags",
                "--quiet",
                str(repo),
                str(target),
            ],
            timeout=600,
        )
        alternates = target / ".git" / "objects" / "info" / "alternates"
        if alternates.exists():
            raise RuntimeError(f"clone unexpectedly has an external object alternate: {alternates}")
        for obj in (target / ".git" / "objects").rglob("*"):
            if obj.is_file() and obj.stat().st_nlink > 1:
                raise RuntimeError(f"clone object is hardlinked to host storage: {obj}")
        for candidate in (ref, f"origin/{ref}"):
            proc = run_managed(
                ["git", "-C", str(target), "checkout", "--detach", "--quiet", candidate],
                timeout=60,
            )
            if proc.ok:
                return target
        raise RuntimeError(f"ref {ref!r} not found in clone of {repo}")
    except BaseException as primary:
        if target.exists():
            try:
                shutil.rmtree(target)
            except OSError as cleanup:
                primary.add_note(f"clone cleanup also failed: {type(cleanup).__name__}: {cleanup}")
        raise

View on GitHub (pinned to d540b00184)

Solutions

  1. Pass `-c clone.defaultResolverName=loose` (or unset any alternates-inducing config) for the clone, or clone from a clean pack of the repo.
  2. Check `git config --system --list` and `--global --list` for object-sharing settings and remove them for the benchmark run.
  3. Re-create the source repo as a normal full clone with no alternates before benchmarking.
  4. Run the harness in the clean container/sandbox image it ships with, where this config is controlled.

Example fix

// before
run_checked(['git','clone','--no-local','--no-hardlinks', str(repo), str(target)])

// after — disable any inherited alternates behavior
run_checked(['git','-c','protocol.version=2','clone','--no-local','--no-hardlinks','--no-tags','--quiet', str(repo), str(target)])
# plus ensure source `repo` has no .git/objects/info/alternates of its own
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path

def clone_has_no_alternates(target: Path) -> bool:
    return not (target / '.git' / 'objects' / 'info' / 'alternates').exists()

Type guard

null

Try / catch

try:
    make_worktree(repo, ref, parent)
except RuntimeError as e:
    if 'external object alternate' in str(e):
        # inspect/fix git config that induced alternates, then retry
        raise
    raise

Prevention

When it happens

Trigger: After clone, `(target/.git/objects/info/alternates).exists()` is True. The clone picked up an alternates file, e.g. because a global git config or template enabled it, or the source repo itself had alternates.

Common situations: A global ~/.gitconfig or /etc/gitconfig sets clone.defaultResolverName or shares objects; the source repo is itself a linked/alternate checkout; a CI image pre-configures object sharing; running against a repo created by `git clone --reference`.

Related errors


AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12). Data as JSON: /api/errors/932dd114af16f6a9. Report an issue: GitHub.