abhigyanpatwari/GitNexus · critical · SandboxError

sandbox_copy path changed or is unreadable: {relative}: {exc

Error message

sandbox_copy path changed or is unreadable: {relative}: {exc}

What it means

TOCTOU guard in _open_child: os.stat succeeded but the subsequent os.open(name, flags, dir_fd=<parent>) raised OSError. The chained exc is typically ENOENT (file deleted between stat and open), EACCES (permissions changed), or ENFILE/EMFILE (fd exhaustion).

Source

Thrown at eval/workflow_bench/task_assets.py:643

    try:
        metadata = os.stat(name, dir_fd=parent_descriptor, follow_symlinks=False)
    except OSError as exc:
        raise SandboxError(f"sandbox_copy path is unavailable: {relative}: {exc}") from exc
    if stat.S_ISLNK(metadata.st_mode):
        raise SandboxError(f"sandbox_copy must not traverse a symlink: {relative}")
    if require_directory and not stat.S_ISDIR(metadata.st_mode):
        raise SandboxError(f"sandbox_copy parent must be a directory: {relative}")
    if not (stat.S_ISDIR(metadata.st_mode) or stat.S_ISREG(metadata.st_mode)):
        raise SandboxError(f"sandbox_copy accepts only regular files and directories: {relative}")
    flags = os.O_RDONLY | getattr(os, "O_CLOEXEC", 0) | getattr(os, "O_NOFOLLOW", 0)
    if stat.S_ISDIR(metadata.st_mode):
        flags |= os.O_DIRECTORY
    else:
        flags |= getattr(os, "O_NONBLOCK", 0)
    try:
        descriptor = os.open(name, flags, dir_fd=parent_descriptor)
    except OSError as exc:
        raise SandboxError(f"sandbox_copy path changed or is unreadable: {relative}: {exc}") from exc
    opened = os.fstat(descriptor)
    if not (stat.S_ISDIR(opened.st_mode) or stat.S_ISREG(opened.st_mode)):
        os.close(descriptor)
        raise SandboxError(f"sandbox_copy accepts only regular files and directories: {relative}")
    if (
        opened.st_dev,
        opened.st_ino,
        stat.S_IFMT(opened.st_mode),
    ) != (
        metadata.st_dev,
        metadata.st_ino,
        stat.S_IFMT(metadata.st_mode),
    ):
        os.close(descriptor)
        raise SandboxError(f"sandbox_copy path changed while opening: {relative}")
    return descriptor

View on GitHub (pinned to d540b00184)

Solutions

  1. Ensure no other process mutates the repo during snapshot capture (one worktree per concurrent arm)
  2. Raise the fd soft limit: `ulimit -n 1048576`
  3. Re-run capture; a transient race is the most common cause
  4. If persistent, audit concurrent writers with lsof or fatrace
Defensive patterns

Strategy: try-catch

Validate before calling

import resource
# raise the fd soft limit before any snapshot capture
soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)
resource.setrlimit(resource.RLIMIT_NOFILE, (min(hard, 1<<20), hard))

Try / catch

from eval.workflow_bench.proposer_sandbox import SandboxError

attempts = 0
while True:
    try:
        snapshot = cache.prepare(task, repo=repo, resolved_sha=sha); break
    except SandboxError as exc:
        if "path changed or is unreadable" in str(exc) and attempts < 2:
            attempts += 1   # transient TOCTOU race; retry with backoff
            continue
        raise

Prevention

When it happens

Trigger: Another process mutates the worktree during snapshot capture; concurrent git checkout; antivirus/scanner holding or deleting files; the process hit its file-descriptor limit mid-walk.

Common situations: Two benchmark arms against the same worktree simultaneously; background indexers rewriting the tree; very deep trees exhausting the fd soft limit.

Related errors


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