abhigyanpatwari/GitNexus · error · ValueError
candidate destination parent must be a real directory: {rela
Error message
candidate destination parent must be a real directory: {relative.parent}: {exc} What it means
Thrown by _replace_regular_file (evolution.py:183) while walking the intermediate parent components of the destination with directory file descriptors using O_RDONLY|O_DIRECTORY|O_NOFOLLOW. Each parent part is mkdir'd (0o700, FileExistsError ignored) then re-opened; if os.open of a part fails with any OSError, the parent is not a real directory — it is a symlink, a regular file, or unreadable.
Source
Thrown at eval/workflow_bench/evolution.py:183
def _replace_regular_file(root: Path, relative: Path, content: bytes) -> None:
"""Replace a clone file through validated directory descriptors."""
if relative.is_absolute() or not relative.parts or ".." in relative.parts:
raise ValueError(f"candidate destination escapes the clone: {relative}")
_require_real_directory(root, label="candidate destination root")
directory_flags = os.O_RDONLY | getattr(os, "O_DIRECTORY", 0) | getattr(os, "O_NOFOLLOW", 0)
descriptor = os.open(root, directory_flags)
try:
for part in relative.parts[:-1]:
try:
os.mkdir(part, mode=0o700, dir_fd=descriptor)
except FileExistsError:
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,View on GitHub (pinned to d540b00184)
Solutions
- Inspect the clone at the named {relative.parent}: remove any symlink/file blocking the directory chain.
- Ensure the clone root and .claude/skills path are writable so missing parents can be created (mode 0o700).
- Reset the clone to a clean checkout and re-apply the overlay.
Example fix
# before: a file blocks the directory chain in the clone
clone/.claude # is a regular file, not a dir -> parent not a real directory
# after: ensure real directories exist
from pathlib import Path
for d in [Path('clone/.claude'), Path('clone/.claude/skills')]:
if d.exists() and not d.is_dir():
d.unlink()
d.mkdir(parents=True, exist_ok=True) Defensive patterns
Strategy: validation
Validate before calling
import os, stat
from pathlib import Path
def clone_parents_are_real_dirs(clone: Path, rel: Path) -> bool:
cur = clone
for part in rel.parts[:-1]:
cur = cur / part
try:
m = cur.lstat().st_mode
except FileNotFoundError:
continue
if stat.S_ISLNK(m) or not stat.S_ISDIR(m):
return False
return True Type guard
null
Try / catch
try:
apply_candidate_overlay(overlay, worktree, sandbox=sandbox)
except ValueError as exc:
if 'parent must be a real directory' in str(exc):
# inspect and repair the named parent path in the clone
... Prevention
- Start from a clean clone so .claude/skills are real directories.
- Ensure the harness user has write permission to create missing parents.
- Never symlink .claude or skills directories in the clone.
When it happens
Trigger: An intermediate path component in the clone is a symlink or regular file instead of a directory (e.g. .claude is a file); the clone lacks write permission to mkdir the missing parent; an existing parent has mode bits that block open.
Common situations: The clone already contains a file named '.claude' or 'skills' where a directory is expected; clone was partially reset or left in a broken state; running as a user without write permission on the clone root.
Related errors
- candidate destination is unreadable: {relative}: {exc}
- candidate destination must be a regular non-symlink file: {r
- short write while staging candidate overlay
- candidate overlay is not a directory: {overlay}
- candidate overlay cannot traverse symlinks: {overlay}
AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12).
Data as JSON: /api/errors/0acf1ec4ffe25497.
Report an issue: GitHub.