{"record":{"id":"44b9413fa6f171e1","repo":"abhigyanpatwari/GitNexus","slug":"overlay-destination-drifted-during-apply-replace","errorCode":null,"errorMessage":"overlay destination drifted during apply: {replacement['target']}","messagePattern":"overlay destination drifted during apply: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"eval/workflow_bench/promotion_apply.py","lineNumber":747,"sourceCode":"        # replacement, then check each member immediately before its swap.\n        _validate_prepared_paths(\n            repo_root,\n            root_descriptor,\n            replacements,\n            phase=\"pre-publication\",\n        )\n        for replacement in replacements:\n            if current_state(replacement) != replacement[\"base_state\"]:\n                raise ValueError(f\"overlay destination drifted before apply: {replacement['target']}\")\n        for replacement in replacements:\n            _validate_prepared_paths(\n                repo_root,\n                root_descriptor,\n                [replacement],\n                phase=\"publication\",\n            )\n            if current_state(replacement) != replacement[\"base_state\"]:\n                raise ValueError(f\"overlay destination drifted during apply: {replacement['target']}\")\n            previous_identity = _entry_identity_at(\n                replacement[\"parent_descriptor\"],\n                replacement[\"name\"],\n            )\n            replacement[\"publication_previous_identity\"] = previous_identity\n            try:\n                _exchange_at(\n                    replacement[\"parent_descriptor\"],\n                    replacement[\"candidate\"],\n                    replacement[\"name\"],\n                )\n            except BaseException:\n                # A wrapper/interruption can raise after the atomic exchange.\n                # Classify by inode movement so a raced edit in the displaced\n                # slot cannot be mistaken for an exchange that never landed.\n                try:\n                    destination_identity = _entry_identity_at(\n                        replacement[\"parent_descriptor\"],","sourceCodeStart":729,"sourceCodeEnd":765,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/d540b00184d71a896261ee02670da9a92d59d8f7/eval/workflow_bench/promotion_apply.py#L729-L765","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Eliminate the concurrent writer (same fixes as error 409 — serialize, lock, disable formatters).","Re-capture `expected_target_bases` from the now-modified tree and retry — the CAS will bind to the new base.","Run promotion in an isolated checkout no other process touches.","If the loop is slow because of many targets, batch them or move the repo to faster storage to shrink the race window."],"exampleFix":"# before: drifted during the per-member publication loop\napply_promoted_overlay(overlay, expected_target_bases=bases)  # -> ValueError during\n# after: hold an exclusive lock across capture+apply so no writer can interleave\nimport fcntl, contextlib\n@contextlib.contextmanager\ndef promote_lock(repo):\n    with open(repo / '.wfbench-promote.lock', 'w') as f:\n        fcntl.flock(f, fcntl.LOCK_EX); yield\nwith promote_lock(repo_root):\n    apply_promoted_overlay(overlay, expected_target_bases=destination_base_digests(overlay))","handlingStrategy":"validation","validationCode":"import fcntl, contextlib\n\n@contextlib.contextmanager\ndef promote_lock(repo_root):\n    with open(repo_root / \".wfbench-promote.lock\", \"w\") as f:\n        fcntl.flock(f, fcntl.LOCK_EX); yield\n\n# the lock must span the ENTIRE apply, not just capture, to block mid-loop drift\nwith promote_lock(repo_root):\n    apply_promoted_overlay(overlay, expected_target_bases=bases)","typeGuard":"null","tryCatchPattern":"try:\n    apply_promoted_overlay(overlay, expected_target_bases=bases)\nexcept ValueError as exc:\n    if \"drifted during apply\" in str(exc):\n        # rollback completed cleanly (see 414); re-capture and retry under lock\n        with promote_lock(repo_root):\n            bases = destination_base_digests(overlay)\n            apply_promoted_overlay(overlay, expected_target_bases=bases)\n    raise","preventionTips":["The lock window must include the whole multi-target apply, not just the pre-checks.","Keep the target set small to shrink the race window if you cannot fully lock.","Move the repo to faster storage if many targets make the apply long."],"tags":["concurrency","drift","integrity","overlay","transaction"],"backgroundTag":null,"analyzedSha":"d540b00184d71a896261ee02670da9a92d59d8f7","analyzedAt":"2026-08-12T19:50:25.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}