abhigyanpatwari/GitNexus · error · ValueError

candidate overlay is not a directory: {overlay}

Error message

candidate overlay is not a directory: {overlay}

What it means

Thrown by candidate_overlay_files (evolution.py:277) when overlay.resolve(strict=True) raises OSError. strict=True fails if the path or any component does not exist (or is a broken symlink). Despite the 'is not a directory' wording, this fires for any resolve failure — the overlay path is missing or unreachable.

Source

Thrown at eval/workflow_bench/evolution.py:277

        command,
        timeout=60,
        env=build_sandbox_environment(),
    )
    return command, result


def candidate_overlay_files(overlay: Path) -> list[Path]:
    """Return a candidate's files after enforcing the benchmark trust boundary.

    Candidates may change only the canonical repo-local skill prompts. They
    cannot modify task code, tests, or verification commands and thereby game
    the promotion gate.
    """
    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:

View on GitHub (pinned to d540b00184)

Solutions

  1. Verify the overlay directory exists and is reachable: assert overlay.exists() and overlay.is_dir() before calling.
  2. Pass an absolute path; re-check for typos and that the artifact was produced.
  3. Ensure the parent volume/mount is present.

Example fix

# before: path typo or missing artifact
apply_candidate_overlay(Path('overlays/candiate'), ...)  # typo

# after: validate existence first
overlay = Path('overlays/candidate').resolve()
assert overlay.exists() and overlay.is_dir(), f'overlay missing: {overlay}'
apply_candidate_overlay(overlay, ...)
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path

def overlay_exists_and_is_dir(p: Path) -> bool:
    return p.exists() and p.is_dir() and not p.is_symlink()

Type guard

from pathlib import Path

def is_real_directory(p: Path) -> bool:
    return p.exists() and p.is_dir() and not p.is_symlink()

Try / catch

try:
    apply_candidate_overlay(overlay, worktree, sandbox=sandbox)
except ValueError as exc:
    if 'is not a directory' in str(exc):
        # verify the path exists; fix typo or regenerate the artifact
        ...

Prevention

When it happens

Trigger: The overlay path does not exist; a parent component is a broken symlink; the path points to a regular file rather than a directory; permission denied traversing to it.

Common situations: Typo or stale relative path passed as the overlay; CI deleted/moved the overlay artifact before the harness read it; pointing at a path inside an unmounted volume.

Related errors


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