abhigyanpatwari/GitNexus · error · ValueError

workspace snapshot directory is unreadable: {directory}: {ex

Error message

workspace snapshot directory is unreadable: {directory}: {exc}

What it means

Raised inside the workspace_snapshot walk (runner_artifacts.py:143) when os.scandir(directory) raises OSError for a subdirectory that lstat earlier reported as a directory. The walker re-wraps the OS error as a ValueError so callers see a single integrity-failure category. This guards against a workspace that mutates its directory structure (revoked permissions, disappeared mount) while being hashed.

Source

Thrown at eval/workflow_bench/runner_artifacts.py:143

    WORKSPACE_SNAPSHOT_BOOTSTRAP_NOISE)."""

    root = worktree.expanduser().absolute()
    mode = root.lstat().st_mode
    if stat.S_ISLNK(mode) or not stat.S_ISDIR(mode) or root.resolve(strict=True) != root:
        raise ValueError(f"workspace snapshot root must be a real directory: {root}")

    snapshot: dict[str, str] = {}
    pending: list[tuple[Path, PurePosixPath]] = [(root, PurePosixPath())]
    entry_count = 0
    path_bytes = 0
    file_bytes = 0
    nofollow = getattr(os, "O_NOFOLLOW", 0)
    while pending:
        directory, relative_dir = pending.pop()
        try:
            children = sorted(os.scandir(directory), key=lambda entry: entry.name, reverse=True)
        except OSError as exc:
            raise ValueError(f"workspace snapshot directory is unreadable: {directory}: {exc}") from exc
        for entry in children:
            relative = relative_dir / entry.name
            if _is_bootstrap_noise(relative):
                continue
            entry_count += 1
            path_bytes += len(relative.as_posix().encode())
            if entry_count > MAX_WORKSPACE_SNAPSHOT_ENTRIES or path_bytes > MAX_WORKSPACE_SNAPSHOT_PATH_BYTES:
                raise ValueError("workspace snapshot exceeds its bounded entry or path limit")
            metadata = entry.stat(follow_symlinks=False)
            permissions = stat.S_IMODE(metadata.st_mode)
            if stat.S_ISDIR(metadata.st_mode):
                snapshot[relative.as_posix()] = f"d:{permissions:o}"
                pending.append((Path(entry.path), relative))
                continue
            if stat.S_ISLNK(metadata.st_mode):
                snapshot[relative.as_posix()] = f"l:{permissions:o}:{os.readlink(entry.path)}"
                continue
            if not stat.S_ISREG(metadata.st_mode):

View on GitHub (pinned to d540b00184)

Solutions

  1. Inspect the {directory} in the message and check its permissions/ownership (ls -ld <dir>); chmod +rx or chown as needed.
  2. Ensure no concurrent process is deleting or re-permissioning worktree subdirs during the snapshot (the snapshot is taken at phase boundaries).
  3. Run the snapshot outside any fuse/network mount; keep the worktree on local disk.

Example fix

# before: a phase leaves a dir unreadable
os.chmod("worktree/secret", 0o000)

# after: restore read/exec so the snapshot can scan it
os.chmod("worktree/secret", 0o755)
Defensive patterns

Strategy: validation

Validate before calling

import os, stat

def assert_scannable(root):
    """Walk and confirm every subdir is rx-accessible before snapshotting."""
    for dirpath, dirnames, _ in os.walk(root):
        try:
            os.scandir(dirpath).close()
        except OSError as exc:
            raise PermissionError(f"unreadable dir {dirpath}: {exc}") from exc
        for d in dirnames:
            mode = os.lstat(os.path.join(dirpath, d)).st_mode
            if not os.access(os.path.join(dirpath, d), os.R_OK | os.X_OK):
                raise PermissionError(f"no rx on {os.path.join(dirpath, d)}")

Try / catch

try:
    snapshot = workspace_snapshot(root)
except ValueError as exc:
    if "directory is unreadable" in str(exc):
        # fix permissions on the named directory, then retry ONCE
        path = str(exc).split(": ")[1]
        os.chmod(path, 0o755)
        snapshot = workspace_snapshot(root)
    else:
        raise

Prevention

When it happens

Trigger: A subdirectory passed the earlier stat S_ISDIR check but os.scandir now fails: permissions were revoked (chmod 000), a fuse/network mount dropped, or the directory was deleted between the stat and the scandir. Also triggered by an unreadable directory the benchmark user lacks rx on.

Common situations: A task setup or model run that chmods a directory to 000 inside the worktree; running the harness as a user without read on a vendored node_modules; a flaky network filesystem backing the worktree.

Related errors


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