abhigyanpatwari/GitNexus · critical · ValueError

overlay destination parent must not be a symlink: {target}

Error message

overlay destination parent must not be a symlink: {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. During the parent walk, an intermediate component is a symlink (`stat.S_ISLNK(metadata.st_mode)`) or not a directory. The walk refuses to descend through symlinks or non-directory entries — every component of the target's parent chain must be a real directory.

Source

Thrown at eval/workflow_bench/promotion_apply.py:203

    except BaseException:
        os.close(descriptor)
        raise
    return root, descriptor


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

View on GitHub (pinned to d540b00184)

Solutions

  1. Locate the offending component: walk `target.parts[:-1]` from root, `lstat` each.
  2. Replace the symlink with the real directory it should name (or remove it).
  3. Re-run promotion once the parent chain is all real directories.

Example fix

// before
# root/.claude/skills/foo -> /shared/foo (symlink)
apply_promoted_overlay(overlay, repo_root=root)
// after
bad = root / '.claude' / 'skills' / 'foo'
if bad.is_symlink():
    bad.unlink(); bad.mkdir()
apply_promoted_overlay(overlay, repo_root=root)
Defensive patterns

Strategy: validation

Validate before calling

def assert_parents_are_real_dirs(root, payload):
    for rel, _ in payload:
        for t in mirror_targets(rel):
            cur = root
            for part in t.parts[:-1]:
                cur = cur / part
                st = cur.lstat()
                if stat.S_ISLNK(st.st_mode) or not stat.S_ISDIR(st.st_mode):
                    raise ValueError(f'parent component not a real dir: {cur}')

Type guard

import stat
def parents_are_real_dirs(root, payload) -> bool:
    for rel, _ in payload:
        for t in mirror_targets(rel):
            cur = root
            for part in t.parts[:-1]:
                cur = cur / part
                try:
                    st = cur.lstat()
                except OSError:
                    return False
                if stat.S_ISLNK(st.st_mode) or not stat.S_ISDIR(st.st_mode):
                    return False
    return True

Try / catch

except ValueError as exc:
    if 'parent must not be a symlink' in str(exc):
        raise SystemExit(f'refuse to descend through symlink in target chain: {exc}')

Prevention

When it happens

Trigger: An intermediate directory in a target's parent chain is a symlink, or has been replaced by a regular file/socket/etc.

Common situations: Someone symlinked a skill category dir for sharing across repos; a broken merge left a file where a directory was expected; a packaging tool wrote a symlink member.

Related errors


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