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
- Resolve to an explicit SHA before calling: `ref = subprocess.check_output(['git','rev-parse', f'{ref}^{{commit}}']).decode().strip()` and pass that SHA.
- Pin `ref` to a fully-qualified branch (`refs/heads/main`) or a tag, and ensure HEAD is not a dangling symref.
- Reproduce the exact rev-parse output and confirm it is clean hex: `git -C <repo> rev-parse '<ref>^{commit}' | cat -A`.
- 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
- Always peel with `^{commit}` and pass the resulting raw SHA, not the symbolic ref, into `committed_destination_base_digests`.
- Avoid passing `HEAD` directly when HEAD may be a symref; resolve it first.
- Add a CI assertion that the captured base is a 40- or 64-char hex string.
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
- committed overlay destination is unavailable: {target}
- ${source}: branch name must not be empty.
- ${source}: branch name is too long (max ${BRANCH_MAX_LENGTH}
- ${source}: branch name must not contain whitespace.
- ${source}: branch name contains characters not allowed in a
AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12).
Data as JSON: /api/errors/a4510bde09d5d8b7.
Report an issue: GitHub.