abhigyanpatwari/GitNexus · error · ValueError
overlay destination parent changed while opening: {target}
Error message
overlay destination parent changed while opening: {target} What it means
All errors below are raised by internal helpers of `eval/workflow_bench/promotion_apply.py` and propagate to the caller of the public entry points: `apply_promoted_overlay(overlay, repo_root, *, expected_digest, expected_target_bases)`, `destination_base_digests(overlay, repo_root)`, `committed_destination_base_digests(overlay, repo_root, *, ref)` and `freeze_overlay(overlay, destination)`. The module applies promoted skill overlays across the canonical skill tree plus its shipped mirrors (`gitnexus/skills`, `gitnexus-claude-plugin/skills`) in a TOCTOU-hardened, symlink-rejecting, descriptor-bound transaction. `os.open(part, flags, dir_fd=current)` (with `O_NOFOLLOW | O_DIRECTORY`) raised `OSError` after `os.stat(part, ...)` succeeded moments before. Between the stat and the open, the intermediate component was replaced/deleted/changed type — a TOCTOU race on a parent directory component.
Source
Thrown at eval/workflow_bench/promotion_apply.py:207
def _open_target_parent(root_descriptor: int, target: PurePosixPath) -> int:
if target.is_absolute() or not target.parts or ".." in target.parts:
raise ValueError(f"overlay destination escapes repository: {target}")
flags = os.O_RDONLY | os.O_DIRECTORY | getattr(os, "O_CLOEXEC", 0) | getattr(os, "O_NOFOLLOW", 0)
current = os.dup(root_descriptor)
try:
for part in target.parts[:-1]:
try:
metadata = os.stat(part, dir_fd=current, follow_symlinks=False)
except OSError as exc:
raise ValueError(f"overlay destination parent is unavailable: {target}") from exc
if stat.S_ISLNK(metadata.st_mode) or not stat.S_ISDIR(metadata.st_mode):
raise ValueError(f"overlay destination parent must not be a symlink: {target}")
try:
child = os.open(part, flags, dir_fd=current)
except OSError as exc:
raise ValueError(f"overlay destination parent changed while opening: {target}") from exc
opened = os.fstat(child)
if (
opened.st_dev,
opened.st_ino,
stat.S_IFMT(opened.st_mode),
) != (
metadata.st_dev,
metadata.st_ino,
stat.S_IFMT(metadata.st_mode),
):
os.close(child)
raise ValueError(f"overlay destination parent changed while opening: {target}")
os.close(current)
current = child
return current
except BaseException:
os.close(current)
raiseView on GitHub (pinned to d540b00184)
Solutions
- Ensure no other process mutates the skill mirror tree during the call.
- Hold an exclusive lock around the checkout for the whole transaction.
- Retry the full entry once from a stable, single-writer checkout.
Defensive patterns
Strategy: retry
Validate before calling
import fcntl
with open(root / '.promotion.lock', 'w') as lock:
fcntl.flock(lock, fcntl.LOCK_EX)
apply_promoted_overlay(overlay, repo_root=root) Try / catch
except ValueError as exc:
if 'parent changed while opening' in str(exc):
log.warning('parent component replaced mid-walk; retrying once')
apply_promoted_overlay(overlay, repo_root=root) Prevention
- Serialize all promotions and tree mutations with a shared lockfile.
- Do not run an editor/indexer over the skill tree during promotion.
- Retry the whole transaction, not a partial sub-step.
When it happens
Trigger: A concurrent process replaced or removed an intermediate directory component between the per-component `os.stat` and the `os.open` in `_open_target_parent`.
Common situations: Concurrent `git checkout`/`rm -rf`/`mv` of a skill directory during promotion; two overlapping promotions; editor or indexer rewriting the tree.
Related errors
- overlay destination parent changed during {phase}: {item['ta
- overlay destination changed while being read: {target}
- repository root changed while opening: {root}
- repository root changed during overlay {phase}: {root}
- Analyzer runtime payload directory is unavailable: ${absolut
AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12).
Data as JSON: /api/errors/fe1c886ba73404b5.
Report an issue: GitHub.