abhigyanpatwari/GitNexus · error · ValueError
overlay destination base binding mismatch:
Error message
overlay destination base binding mismatch:
What it means
Thrown in `apply_promoted_overlay` when the caller passed `expected_target_bases` and the on-disk base digests differ from what was expected. The message enumerates which paths are missing, unexpected, or drifted, so you can see exactly how the working tree diverged from the promotion evidence.
Source
Thrown at eval/workflow_bench/promotion_apply.py:646
repo_root, root_descriptor, prepared = _prepare_targets(payload, repo_root)
current_bases = {item["target"].as_posix(): item["base_digest"] for item in prepared}
if expected_target_bases is not None and expected_target_bases != current_bases:
expected_paths = set(expected_target_bases)
current_paths = set(current_bases)
missing = sorted(current_paths - expected_paths)
unexpected = sorted(expected_paths - current_paths)
drifted = sorted(
path for path in current_paths & expected_paths if current_bases[path] != expected_target_bases[path]
)
details = []
if missing:
details.append("missing=" + ",".join(missing))
if unexpected:
details.append("unexpected=" + ",".join(unexpected))
if drifted:
details.append("drifted=" + ",".join(drifted))
_close_prepared(root_descriptor, prepared)
raise ValueError("overlay destination base binding mismatch: " + "; ".join(details))
replacements: list[dict[str, Any]] = []
completed: list[dict[str, Any]] = []
preserve_backups = False
published_all = False
rollback_complete = False
def entry_state(replacement: dict[str, Any], name: str) -> tuple[str, int]:
current, mode = _read_destination_at(
replacement["parent_descriptor"],
name,
target=replacement["target"],
)
return hashlib.sha256(current).hexdigest(), stat.S_IMODE(mode)
def current_state(replacement: dict[str, Any]) -> tuple[str, int]:
return entry_state(replacement, replacement["name"])
View on GitHub (pinned to d540b00184)
Solutions
- Read the message: `drifted=` paths need re-capture, `missing=` paths are new on disk, `unexpected=` paths are gone — handle each category.
- Re-capture `expected_target_bases` with `destination_base_digests(overlay)` immediately before apply and pass the fresh dict.
- Restore the worktree to the state evidence was captured from (e.g. `git checkout -- .` or `git restore --source=<ref>`).
- Ensure no concurrent writer (formatter, IDE, build) touches the repo between capture and apply.
Example fix
# before: stale expected_target_bases from an earlier capture apply_promoted_overlay(overlay, expected_target_bases=stale_bases) # -> ValueError # after: re-capture fresh and apply atomically from workflow_bench.promotion_apply import destination_base_digests fresh = destination_base_digests(overlay) apply_promoted_overlay(overlay, expected_target_bases=fresh)
Defensive patterns
Strategy: validation
Validate before calling
from workflow_bench.promotion_apply import destination_base_digests
def current_target_bases_match(overlay, expected: dict[str, str]) -> bool:
return destination_base_digests(overlay) == expected
# capture right before apply
bases = destination_base_digests(overlay)
assert current_target_bases_match(overlay, bases)
apply_promoted_overlay(overlay, expected_target_bases=bases) Type guard
null
Try / catch
try:
apply_promoted_overlay(overlay, expected_target_bases=expected)
except ValueError as exc:
if "base binding mismatch" in str(exc):
# parse drifted=/missing=/unexpected= and re-capture
bases = destination_base_digests(overlay)
raise Prevention
- Always capture `expected_target_bases` immediately before apply, in the same locked section.
- Freeze the worktree (no edits, no formatters) between capture and apply.
- Log the diff string from the message so you can see which paths drift when it happens.
When it happens
Trigger: Calling `apply_promoted_overlay(overlay, expected_target_bases=<dict>)` after the destination files changed on disk (paths edited, added, removed, or their mirror targets changed). The diff of expected vs. current base digests is non-empty and the detailed `missing=`/`unexpected=`/`drifted=` breakdown is appended.
Common situations: A file was edited in the worktree after `destination_base_digests` was captured; a new file was added to the tree; a file was deleted; the overlay's `mirror_targets` mapping changed so the target set no longer matches; running apply against a different branch than the one evidence was captured on.
Related errors
- candidate overlay digest no longer matches promotion evidenc
- overlay destination drifted before apply: {replacement['targ
- overlay destination drifted during apply: {replacement['targ
- frozen overlay bytes do not match the authorized input
- overlay destination escapes repository: {target}
AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12).
Data as JSON: /api/errors/66f5ff8ced0de30d.
Report an issue: GitHub.