abhigyanpatwari/GitNexus · error · ValueError

overlay destination drifted during apply: {replacement['targ

Error message

overlay destination drifted during apply: {replacement['target']}

What it means

Thrown in the per-replacement publication loop of `apply_promoted_overlay`. Right before each individual `_exchange_at` swap, the target's current state is re-checked against its base state; if it drifted mid-transaction (i.e. between the pre-publication check at :737 and this per-member check at :746), the apply aborts and rolls back completed swaps.

Source

Thrown at eval/workflow_bench/promotion_apply.py:747

        # replacement, then check each member immediately before its swap.
        _validate_prepared_paths(
            repo_root,
            root_descriptor,
            replacements,
            phase="pre-publication",
        )
        for replacement in replacements:
            if current_state(replacement) != replacement["base_state"]:
                raise ValueError(f"overlay destination drifted before apply: {replacement['target']}")
        for replacement in replacements:
            _validate_prepared_paths(
                repo_root,
                root_descriptor,
                [replacement],
                phase="publication",
            )
            if current_state(replacement) != replacement["base_state"]:
                raise ValueError(f"overlay destination drifted during apply: {replacement['target']}")
            previous_identity = _entry_identity_at(
                replacement["parent_descriptor"],
                replacement["name"],
            )
            replacement["publication_previous_identity"] = previous_identity
            try:
                _exchange_at(
                    replacement["parent_descriptor"],
                    replacement["candidate"],
                    replacement["name"],
                )
            except BaseException:
                # A wrapper/interruption can raise after the atomic exchange.
                # Classify by inode movement so a raced edit in the displaced
                # slot cannot be mistaken for an exchange that never landed.
                try:
                    destination_identity = _entry_identity_at(
                        replacement["parent_descriptor"],

View on GitHub (pinned to d540b00184)

Solutions

  1. Eliminate the concurrent writer (same fixes as error 409 — serialize, lock, disable formatters).
  2. Re-capture `expected_target_bases` from the now-modified tree and retry — the CAS will bind to the new base.
  3. Run promotion in an isolated checkout no other process touches.
  4. If the loop is slow because of many targets, batch them or move the repo to faster storage to shrink the race window.

Example fix

# before: drifted during the per-member publication loop
apply_promoted_overlay(overlay, expected_target_bases=bases)  # -> ValueError during
# after: hold an exclusive lock across capture+apply so no writer can interleave
import fcntl, contextlib
@contextlib.contextmanager
def promote_lock(repo):
    with open(repo / '.wfbench-promote.lock', 'w') as f:
        fcntl.flock(f, fcntl.LOCK_EX); yield
with promote_lock(repo_root):
    apply_promoted_overlay(overlay, expected_target_bases=destination_base_digests(overlay))
Defensive patterns

Strategy: validation

Validate before calling

import fcntl, contextlib

@contextlib.contextmanager
def promote_lock(repo_root):
    with open(repo_root / ".wfbench-promote.lock", "w") as f:
        fcntl.flock(f, fcntl.LOCK_EX); yield

# the lock must span the ENTIRE apply, not just capture, to block mid-loop drift
with promote_lock(repo_root):
    apply_promoted_overlay(overlay, expected_target_bases=bases)

Type guard

null

Try / catch

try:
    apply_promoted_overlay(overlay, expected_target_bases=bases)
except ValueError as exc:
    if "drifted during apply" in str(exc):
        # rollback completed cleanly (see 414); re-capture and retry under lock
        with promote_lock(repo_root):
            bases = destination_base_digests(overlay)
            apply_promoted_overlay(overlay, expected_target_bases=bases)
    raise

Prevention

When it happens

Trigger: A concurrent writer modified a target file after that target passed the pre-publication check but before its own exchange. Different from error 409 — here the drift happened *during* the multi-target apply loop, not before it.

Common situations: Long apply with many targets where a background process edits one of the later targets while earlier ones are being swapped; IDE autosave; CI running a formatter mid-apply; another promotion overlapping.

Related errors


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