abhigyanpatwari/GitNexus · error · SandboxError
sandbox_copy accepts only regular files and directories: {re
Error message
sandbox_copy accepts only regular files and directories: {relative} What it means
Raised by _SnapshotBuilder.copy_descriptor when a declared sandbox_copy path resolves to a filesystem entry that is neither a regular file nor a directory (e.g. a FIFO, socket, character/block device, or other special file). The snapshot pipeline can only freeze regular files and directories because it reflinks or buffered-copies bytes and records a sha256; special files have no stable byte content to capture. This guard fires after the directory branch and just before _copy_file, so it is the final type gate on the descriptor that was opened with O_NOFOLLOW.
Source
Thrown at eval/workflow_bench/task_assets.py:413
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):
raise SandboxError(f"sandbox_copy accepts only regular files and directories: {relative}")
self._copy_file(descriptor, relative, before)
def _record_directory(self, relative: PurePosixPath) -> None:
self._ensure_parents(relative.parent)
self._record(AssetManifestEntry(path=relative, kind="directory"))
destination = self.destination / Path(*relative.parts)
destination.mkdir(mode=0o700, exist_ok=True)
def _copy_file(self, descriptor: int, relative: PurePosixPath, before: os.stat_result) -> None:
self._ensure_parents(relative.parent)
if self.budget.total_bytes + before.st_size > MAX_TASK_ASSET_BYTES:
raise SandboxError("sandbox_copy exceeds the total byte limit")
destination = self.destination / Path(*relative.parts)
flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL | getattr(os, "O_CLOEXEC", 0)
output = os.open(destination, flags, 0o600)
digest = hashlib.sha256()
copied = 0
try:View on GitHub (pinned to d540b00184)
Solutions
- Inspect the declared path with `find <path> -type p -o -type s -o -type b -o -type c` and remove or exclude the offending special files from the declaration.
- Narrow the sandbox_copy declaration to the specific subdirectories or files you need instead of a broad root that sweeps in sockets/pipes.
- If the special file is a legitimate runtime artifact, ensure it is created inside the arm clone at runtime, not captured in the immutable snapshot.
- Re-run the benchmark after cleaning the repo working tree (`git clean -fdx`) to eliminate stray special files left by prior tooling.
Example fix
// before (task definition)
{"sandbox_copy": ["repo/build"]} // build/ contains a leftover FIFO
// after
{"sandbox_copy": ["repo/build/dist", "repo/build/config.json"]}
// or remove the FIFO: rm -f repo/build/.lock-fifo Defensive patterns
Strategy: validation
Validate before calling
from pathlib import Path, PurePosixPath
import stat, os
def validate_no_special_files(repo: Path, declarations: list[str]) -> None:
for raw in declarations:
root = repo / raw
for current, dirs, files in os.walk(root, followlinks=False):
for name in files:
p = Path(current) / name
mode = p.lstat().st_mode
if not stat.S_ISREG(mode):
raise ValueError(f"non-regular file in sandbox_copy: {p} (mode {oct(stat.S_IFMT(mode))})")
for name in dirs:
p = Path(current) / name
if not stat.S_ISDIR(p.lstat().st_mode):
raise ValueError(f"non-directory entry in sandbox_copy dir list: {p}")
# run before TaskAssetCache.prepare
validate_no_special_files(repo_path, task["sandbox_copy"]) Type guard
import stat
from pathlib import Path
def is_regular_or_dir(p: Path) -> bool:
m = p.lstat().st_mode
return stat.S_ISREG(m) or stat.S_ISDIR(m) Try / catch
from eval.workflow_bench.propposer_sandbox import SandboxError
try:
snapshot = cache.prepare(task, repo=repo, resolved_sha=sha)
except SandboxError as exc:
if "accepts only regular files" in str(exc):
# scan and clean the declared tree, then re-run
...
raise Prevention
- Run `git clean -fdx` on the repo before capture to remove stray sockets, pipes, and device nodes left by prior tooling.
- Avoid declaring broad roots; declare the specific files and directories the task reads.
- Do not run database servers, X servers, or socket-creating daemons with runtimes inside declared sandbox_copy paths.
When it happens
Trigger: A task declares sandbox_copy of a path that contains a Unix special file: `os.mkfifo`, `socket.socket(AF_UNIX).bind(...)`, or a device node under `/dev` bind-mounted into the repo. Also triggered when a regular file is replaced with a special file between the _open_child stat and the fstat in copy_descriptor (a TOCTOU swap), though that path is more commonly hit via the 'changed while snapshotting' guards.
Common situations: Accidentally declaring a sandbox_copy root that includes a build artifact directory containing named pipes or sockets (e.g. leftover `.npm/_cacache` locks, postgres test harness sockets, X11-style `/tmp/.X11-unix` binds). Declaring a path that a previous test run left a FIFO in. Container setups where the repo is overlaid with device nodes.
Related errors
- evidence path is unreadable: {value}: {exc}
- {label} must be a real directory: {lexical}: {exc}
- {label} must be a real directory: {lexical}
- {label} must not traverse symlinks: {lexical}
- Compound Engineering plugin file must be regular and non-sym
AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12).
Data as JSON: /api/errors/26ade66c46fb86d3.
Report an issue: GitHub.