abhigyanpatwari/GitNexus · critical · ValueError

candidate destination must be a regular non-symlink file: {r

Error message

candidate destination must be a regular non-symlink file: {relative}

What it means

Thrown by _replace_regular_file (evolution.py:197) when the existing leaf in the clone is a symlink or any non-regular file (directory, fifo, device). The harness only atomically os.replace's a staged temp file onto a regular non-symlink file (or a not-yet-existing leaf); it will never overwrite a symlink, because that could redirect the write outside the clone.

Source

Thrown at eval/workflow_bench/evolution.py:197

                pass
            try:
                child = os.open(part, directory_flags, dir_fd=descriptor)
            except OSError as exc:
                raise ValueError(
                    f"candidate destination parent must be a real directory: {relative.parent}: {exc}"
                ) from exc
            os.close(descriptor)
            descriptor = child

        leaf = relative.name
        try:
            existing = os.stat(leaf, dir_fd=descriptor, follow_symlinks=False)
        except FileNotFoundError:
            existing = None
        except OSError as exc:
            raise ValueError(f"candidate destination is unreadable: {relative}: {exc}") from exc
        if existing is not None and (stat.S_ISLNK(existing.st_mode) or not stat.S_ISREG(existing.st_mode)):
            raise ValueError(f"candidate destination must be a regular non-symlink file: {relative}")

        temporary = f".wfbench-overlay-{secrets.token_hex(12)}"
        temp_descriptor = os.open(
            temporary,
            os.O_WRONLY | os.O_CREAT | os.O_EXCL | getattr(os, "O_NOFOLLOW", 0),
            0o600,
            dir_fd=descriptor,
        )
        try:
            view = memoryview(content)
            while view:
                written = os.write(temp_descriptor, view)
                if written <= 0:
                    raise OSError("short write while staging candidate overlay")
                view = view[written:]
            os.fchmod(temp_descriptor, 0o644)
        except BaseException:
            try:

View on GitHub (pinned to d540b00184)

Solutions

  1. Remove the offending symlink/non-regular file at the leaf path in the clone.
  2. Reset the clone to a clean checkout (git clean -fdx / fresh clone) so the destination is either absent or a real regular file.
  3. Verify with os.path.islink and stat.S_ISREG before invoking the harness.

Example fix

# before: clone has a symlink at the destination
clone/.claude/skills/gitnexus-work -> /shared/skills  # symlink -> rejected

# after: replace with a real regular file (or delete it)
import os, stat
p = clone / '.claude/skills/gitnexus-work/SKILL.md'
if os.path.islink(p) or not stat.S_ISREG(p.lstat().st_mode):
    os.unlink(p)  # harness will create a fresh regular file
Defensive patterns

Strategy: validation

Validate before calling

import os, stat
from pathlib import Path

def leaf_is_regular_or_absent(clone: Path, rel: Path) -> bool:
    leaf = clone / rel
    try:
        m = leaf.lstat().st_mode
    except FileNotFoundError:
        return True
    return stat.S_ISREG(m) and not stat.S_ISLNK(m)

Type guard

import os, stat
from pathlib import Path

def is_regular_or_absent(p: Path) -> bool:
    try:
        m = p.lstat().st_mode
    except FileNotFoundError:
        return True
    return stat.S_ISREG(m) and not stat.S_ISLNK(m)

Try / catch

try:
    apply_candidate_overlay(overlay, worktree, sandbox=sandbox)
except ValueError as exc:
    if 'regular non-symlink file' in str(exc):
        # unlink the symlink/dir at the named path, then retry
        ...

Prevention

When it happens

Trigger: The clone already has a symlink, directory, fifo, or device node at the exact overlay destination path. A symlink leaf is treated as a security violation because os.replace would follow/replace the link target rather than the link slot.

Common situations: A previous tool symlinked .claude/skills/gitnexus-work to a shared dir; the clone ships a symlink at that path; leftover directories from a malformed prior overlay.

Related errors


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