abhigyanpatwari/GitNexus · error · SandboxError

sandbox_copy directory is unreadable: {relative}: {exc}

Error message

sandbox_copy directory is unreadable: {relative}: {exc}

What it means

While snapshotting, os.listdir(descriptor) on a directory raised OSError, wrapped as a SandboxError with {relative} and {exc}. The snapshotter cannot enumerate the directory, so capture cannot be proven complete.

Source

Thrown at eval/workflow_bench/task_assets.py:394

        budget: _SnapshotBudget | None = None,
        allow_symlinks: bool = False,
        preserve_modes: bool = False,
    ):
        self.destination = destination
        self.entries: dict[PurePosixPath, AssetManifestEntry] = {}
        self.total_bytes = 0
        self.budget = budget if budget is not None else _SnapshotBudget()
        self.allow_symlinks = allow_symlinks
        self.preserve_modes = preserve_modes

    def copy_descriptor(self, descriptor: int, relative: PurePosixPath) -> None:
        before = os.fstat(descriptor)
        if stat.S_ISDIR(before.st_mode):
            self._record_directory(relative)
            try:
                names = sorted(os.listdir(descriptor))
            except OSError as exc:
                raise SandboxError(f"sandbox_copy directory is unreadable: {relative}: {exc}") from exc
            for name in names:
                child_relative = relative / name
                child_metadata = os.stat(name, dir_fd=descriptor, follow_symlinks=False)
                if stat.S_ISLNK(child_metadata.st_mode):
                    if not self.allow_symlinks:
                        raise SandboxError(f"sandbox_copy must not traverse a symlink: {child_relative}")
                    self._copy_symlink(descriptor, name, child_relative, child_metadata)
                    continue
                child = _open_child(descriptor, name, child_relative)
                try:
                    self.copy_descriptor(child, child_relative)
                finally:
                    os.close(child)
            after = os.fstat(descriptor)
            if _mutation_identity(before) != _mutation_identity(after):
                raise SandboxError(f"sandbox_copy directory changed while snapshotting: {relative}")
            return
        if not stat.S_ISREG(before.st_mode):

View on GitHub (pinned to d540b00184)

Solutions

  1. Inspect the chained OSError.errno (EACCES/EIO/ENOENT).
  2. Fix permissions on the source tree (read+execute on directories, read on files).
  3. Run on a reliable filesystem and ensure no concurrent deletion during capture.
  4. Re-run prepare once the tree is stable.
Defensive patterns

Strategy: try-catch

Validate before calling

import os

def assert_source_listable(repo, relative):
    p = os.path.join(repo, *relative.parts)
    try:
        with os.scandir(p) as it:
            list(it)
    except OSError as exc:
        raise RuntimeError(f"sandbox_copy dir unreadable: {relative}: {exc}") from exc

Try / catch

from workflow_bench.proposer_sandbox import SandboxError

try:
    snapshot = cache.prepare(task, repo=repo, resolved_sha=sha)
except SandboxError as exc:
    if "directory is unreadable" in str(exc):
        log.error("sandbox_copy permission/IO error - check dir r-x bits and filesystem health")
    raise

Prevention

When it happens

Trigger: A sandbox_copy source directory lacks read/execute permission for the runner, or hits an IO error (EIO) or vanishes (ENOENT) during TaskAssetCache.prepare.

Common situations: Permission bits on a vendored/generated directory; a mount returning EIO; a directory removed mid-snapshot by a concurrent writer.

Related errors


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