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
- Remove the offending symlink/non-regular file at the leaf path in the clone.
- Reset the clone to a clean checkout (git clean -fdx / fresh clone) so the destination is either absent or a real regular file.
- 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
- Never symlink the destination skill files in the clone.
- git clean -fdx the clone before benchmark runs to remove stray nodes.
- Treat this as a security signal: a symlink leaf could redirect writes.
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
- candidate overlay cannot traverse symlinks: {overlay}
- candidate overlay cannot contain symlinks: {relative}
- candidate overlay entries must be regular files: {relative}
- overlay destination parent must not be a symlink: {target}
- Refusing to delete ${dbPath}: resolved path ${realPath} is o
AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12).
Data as JSON: /api/errors/80c287d6a17a947b.
Report an issue: GitHub.