can1357/oh-my-pi · warning · HeadDriftError

HEAD changed since preflight ({expected_head[:12]} → {head[:

Error message

HEAD changed since preflight ({expected_head[:12]} → {head[:12]}); aborting push.

What it means

`push` re-checks HEAD with `rev_parse_head` immediately before pushing; if it differs from the `expected_head` captured during preflight, it raises `HeadDriftError` (a GitCommandError subclass with code 128) and aborts the push. This guards against pushing a commit someone else (or another step) added to the local branch after the preflight snapshot — preventing unintended or unsafe pushes.

Source

Thrown at python/robomp/src/git_ops.py:808

    history rewrites (e.g. the agent doing `git commit --amend --reset-author
    --no-edit` to fix author identity) while still refusing the push if origin
    has moved since our last fetch — i.e. it never clobbers work the bot
    didn't see.

    When `expected_head` is supplied, this verifies the *local* HEAD matches
    before pushing — anything else means an unexpected commit raced in inside
    our own worktree between the orchestrator's preflight and this call, and
    the push is aborted with `HeadDriftError`. This is a separate concern from
    `--force-with-lease`, which compares against the remote ref.
    """
    slot_kwargs = _slot_subprocess_kwargs(slot_uid)
    git_safe_directory = safe_directory
    if git_safe_directory is None and slot_kwargs:
        git_safe_directory = repo_dir

    head = rev_parse_head(repo_dir, safe_directory=git_safe_directory, **slot_kwargs)
    if expected_head and head != expected_head:
        raise HeadDriftError(
            ["git", "push"],
            128,
            "",
            f"HEAD changed since preflight ({expected_head[:12]} → {head[:12]}); aborting push.",
        )
    # Probe the local remote-tracking ref. Missing → first push; we pin the
    # lease to the empty value so the push only succeeds if origin still has
    # no `<branch>`. Present → pin to that SHA.
    probe = _run_git(
        ["rev-parse", "--verify", "--quiet", f"refs/remotes/origin/{branch}"],
        cwd=repo_dir,
        token=None,
        safe_directory=git_safe_directory,
        **slot_kwargs,
    )
    expected_remote = probe.stdout.strip() if probe.returncode == 0 else ""
    if remote_url is None:
        push_extra_env: dict[str, str] | None = None

View on GitHub (pinned to 9690622007)

Solutions

  1. Re-run the push operation from the start so preflight captures the current HEAD
  2. Check `git log`/`git reflog` to see what moved HEAD — if the new commit is unwanted, reset it before retrying
  3. Ensure no concurrent processes (other tool runs, IDE auto-commit) operate on the repo during push
  4. If the drift is expected and safe, pass/refresh the expected_head to the current HEAD value

Example fix

// before
expected = rev_parse_head(repo_dir)
...long work...
push(repo_dir, expected_head=expected)  # HeadDriftError
// after
expected = rev_parse_head(repo_dir)
push(repo_dir, expected_head=expected)  # preflight and push adjacent; retry whole flow on drift
Defensive patterns

Strategy: retry

Validate before calling

current = rev_parse_head(repo_dir)
if expected_head and current != expected_head:
    # refresh preflight instead of pushing
    expected_head = current

Try / catch

from tenacity import retry, retry_if_exception_type, stop_after_attempt
@retry(retry=retry_if_exception_type(HeadDriftError), stop=stop_after_attempt(2))
def safe_push(repo_dir):
    expected = rev_parse_head(repo_dir)
    push(repo_dir, expected_head=expected)

Prevention

When it happens

Trigger: Another process/IDE/hook commits or amends on the current branch between the preflight HEAD capture and the push call; an auto-formatter or pre-commit hook creating commits; concurrent runs of the tooling on the same repo.

Common situations: Long gap between preflight and push while the developer works in the repo, CI job sharing a checkout with another job, hooks that amend or create fixup commits automatically.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/0fc792ee17464f87. Report an issue: GitHub.