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
- Inspect the {directory} in the message and check its permissions/ownership (ls -ld <dir>); chmod +rx or chown as needed.
- Ensure no concurrent process is deleting or re-permissioning worktree subdirs during the snapshot (the snapshot is taken at phase boundaries).
- 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
- Ensure the benchmark user has rx on every directory under the worktree.
- Stop background processes that re-permission or delete worktree subdirs during phase boundaries.
- Run the snapshot only when the phase is quiescent.
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
- Could not read ${GITNEXUS_RC_FILENAME}: ${(err as Error).mes
- candidate destination is unreadable: {relative}: {exc}
- candidate overlay directory is unreadable: {directory}: {exc
- transcript artifact parent must be owner-only: {current}
- oracle source is unreadable: {relative}
AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12).
Data as JSON: /api/errors/104cadcf17fd386e.
Report an issue: GitHub.