abhigyanpatwari/GitNexus · error · ValueError
candidate overlay directory is unreadable: {directory}: {exc
Error message
candidate overlay directory is unreadable: {directory}: {exc} What it means
Thrown by candidate_overlay_files (evolution.py:291) when os.scandir of a directory inside the overlay tree raises OSError during the recursive walk. The top-level directory is already validated; this fires on a subdirectory that cannot be listed — typically a permissions problem (missing read or execute bit) or an I/O error.
Source
Thrown at eval/workflow_bench/evolution.py:291
overlay = overlay.expanduser().absolute()
try:
resolved_overlay = overlay.resolve(strict=True)
except OSError as exc:
raise ValueError(f"candidate overlay is not a directory: {overlay}") from exc
if resolved_overlay != overlay:
raise ValueError(f"candidate overlay cannot traverse symlinks: {overlay}")
_require_real_directory(overlay, label="candidate overlay")
entries: list[Path] = []
pending = [overlay]
entry_count = 0
while pending:
directory = pending.pop()
child_directories: list[Path] = []
try:
iterator = os.scandir(directory)
except OSError as exc:
raise ValueError(f"candidate overlay directory is unreadable: {directory}: {exc}") from exc
with iterator:
for item in iterator:
entry_count += 1
if entry_count > MAX_CANDIDATE_ENTRIES:
raise ValueError(f"candidate overlay exceeds the {MAX_CANDIDATE_ENTRIES}-entry limit")
path = Path(item.path)
relative = path.relative_to(overlay)
if len(relative.as_posix().encode()) > MAX_CANDIDATE_PATH_BYTES:
raise ValueError(f"candidate overlay path exceeds {MAX_CANDIDATE_PATH_BYTES} bytes: {relative}")
if item.is_symlink():
raise ValueError(f"candidate overlay cannot contain symlinks: {relative}")
if item.is_dir(follow_symlinks=False):
child_directories.append(path)
continue
if not item.is_file(follow_symlinks=False):
raise ValueError(f"candidate overlay entries must be regular files: {relative}")
entries.append(path)
if len(entries) > MAX_CANDIDATE_FILES:View on GitHub (pinned to d540b00184)
Solutions
- Fix directory permissions across the overlay so every dir is readable+traversable: find overlay -type d -exec chmod u+rx {} +.
- Re-copy the overlay preserving directory execute bits.
- Move the overlay onto a healthy local filesystem.
Example fix
# before: a subdir is unreadable -> scandir raises EACCES
# after: ensure every directory is r-x before running
import subprocess
subprocess.run(['find', str(overlay), '-type', 'd', '-exec', 'chmod', 'u+rxX', {} +'])
# or in Python:
for d in overlay.rglob('*'):
if d.is_dir():
d.chmod(0o755) Defensive patterns
Strategy: validation
Validate before calling
import os
from pathlib import Path
def overlay_dirs_listable(root: Path) -> bool:
for d in [root, *root.rglob('*')]:
if d.is_dir():
try:
list(os.scandir(d))
except OSError:
return False
return True Type guard
null
Try / catch
try:
apply_candidate_overlay(overlay, worktree, sandbox=sandbox)
except ValueError as exc:
if 'directory is unreadable' in str(exc):
# chmod u+rx on overlay dirs, then retry
... Prevention
- Give every overlay directory read+execute bits (chmod -R u+rxX).
- Copy overlays with a tool that preserves directory execute bits.
- Keep the overlay on a healthy local filesystem.
When it happens
Trigger: A subdirectory of the overlay lacks read or execute permission (EACCES), or scandir hits an I/O error mid-walk (failing disk, vanished mount).
Common situations: Overlay created with restrictive umask leaving a subdir mode 0o300 or 0o600; copy from a source that dropped execute bits on directories; overlay partially on an unmounted network share.
Related errors
- candidate destination is unreadable: {relative}: {exc}
- Could not read ${GITNEXUS_RC_FILENAME}: ${(err as Error).mes
- candidate destination parent must be a real directory: {rela
- candidate destination must be a regular non-symlink file: {r
- short write while staging candidate overlay
AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12).
Data as JSON: /api/errors/33bbf676d887c6ad.
Report an issue: GitHub.