abhigyanpatwari/GitNexus · error · ValueError

committed promotion base is not an immutable object id

Error message

committed promotion base is not an immutable object id

What it means

Thrown in `committed_destination_base_digests` when `git rev-parse` succeeded (rev.ok, captured stdout) but the decoded output is empty or contains characters outside `[0-9a-fA-F]`. The promoter treats this as 'the resolved object is not a 40/64-char hex object id' and refuses to bind promotion evidence to it.

Source

Thrown at eval/workflow_bench/promotion_apply.py:538

    repo_root: Path = REPO_ROOT,
    *,
    ref: str = "HEAD",
) -> dict[str, str]:
    """Bind targets to one immutable committed incumbent, never live edits."""

    _, payload = candidate_overlay_payload(overlay)
    root, root_descriptor = _open_repository_root(repo_root)
    os.close(root_descriptor)
    rev = run_managed(
        ["git", "-C", str(root), "rev-parse", f"{ref}^{{commit}}"],
        timeout=60,
        capture_stdout_bytes=256,
    )
    if not rev.ok or rev.stdout_capture_overflow or rev.stdout_capture is None:
        raise ValueError("could not resolve the committed promotion base")
    commit = rev.stdout_capture.decode("ascii", errors="strict").strip()
    if not commit or any(character not in "0123456789abcdefABCDEF" for character in commit):
        raise ValueError("committed promotion base is not an immutable object id")
    bindings: dict[str, str] = {}
    for relative, _content in payload:
        for target in mirror_targets(relative):
            key = target.as_posix()
            if key in bindings:
                raise ValueError(f"duplicate overlay destination: {target}")
            result = run_managed(
                ["git", "-C", str(root), "show", f"{commit}:{key}"],
                timeout=60,
                capture_stdout_bytes=MAX_CANDIDATE_OVERLAY_BYTES + 1,
            )
            if not result.ok or result.stdout_capture_overflow or result.stdout_capture is None:
                raise ValueError(f"committed overlay destination is unavailable: {target}")
            bindings[key] = hashlib.sha256(result.stdout_capture).hexdigest()
    return bindings


def _write_recovery_artifact(

View on GitHub (pinned to d540b00184)

Solutions

  1. Resolve to an explicit SHA before calling: `ref = subprocess.check_output(['git','rev-parse', f'{ref}^{{commit}}']).decode().strip()` and pass that SHA.
  2. Pin `ref` to a fully-qualified branch (`refs/heads/main`) or a tag, and ensure HEAD is not a dangling symref.
  3. Reproduce the exact rev-parse output and confirm it is clean hex: `git -C <repo> rev-parse '<ref>^{commit}' | cat -A`.
  4. If the repo uses worktrees, resolve from the main worktree to avoid symref leakage.

Example fix

# before
bases = committed_destination_base_digests(overlay, ref='HEAD')  # HEAD is symbolic
# after
sha = subprocess.check_output(
    ['git', '-C', str(repo_root), 'rev-parse', 'HEAD^{commit}']
).decode().strip()
bases = committed_destination_base_digests(overlay, ref=sha)
Defensive patterns

Strategy: validation

Validate before calling

import re, subprocess

_HEX = re.compile(r"^[0-9a-fA-F]+$")

def ref_is_immutable_object_id(repo_root, ref: str) -> bool:
    out = subprocess.run(
        ["git", "-C", str(repo_root), "rev-parse", f"{ref}^{{commit}}"],
        capture_output=True, text=True,
    )
    sha = out.stdout.strip()
    return out.returncode == 0 and bool(sha) and bool(_HEX.match(sha))

# resolve once, pass the SHA everywhere after
sha = subprocess.check_output(
    ["git", "-C", str(repo_root), "rev-parse", f"{ref}^{{commit}}"]
).decode().strip()
committed_destination_base_digests(overlay, ref=sha)

Type guard

null

Try / catch

null

Prevention

When it happens

Trigger: Calling `committed_destination_base_digests` with a ref that resolves to something other than a plain SHA — for example a symbolic ref printed as `ref: refs/heads/main`, a relative path, a `refs/...` textual name, or output that includes extra annotation lines. The hex-character scan rejects anything not a raw object id.

Common situations: Passing `ref='HEAD'` when HEAD is symbolic and rev-parse returned the symref text instead of the commit; using a ref expression whose output carries extra metadata; a corrupted or unusual git state; the `^{commit}` peeling did not yield a clean SHA (rare, e.g. a broken alternates setup).

Related errors


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