abhigyanpatwari/GitNexus · error · SandboxError

sandbox_copy exceeds the total byte limit

Error message

sandbox_copy exceeds the total byte limit

What it means

Raised by _SnapshotBuilder._copy_file as a pre-flight check before streaming any bytes: if the shared budget's total_bytes plus the file's reported st_size would exceed MAX_TASK_ASSET_BYTES (2 GiB), snapshotting aborts before opening the output. This is the early, size-based rejection — it uses the filesystem's reported size, not bytes actually read. It exists so a grossly oversized declaration fails fast rather than after a partial multi-hundred-megabyte copy.

Source

Thrown at eval/workflow_bench/task_assets.py:425

                    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:
            while True:
                chunk = _read_source_chunk(descriptor, COPY_CHUNK_BYTES)
                if not chunk:
                    break
                copied += len(chunk)
                if self.budget.total_bytes + copied > MAX_TASK_ASSET_BYTES:
                    raise SandboxError("sandbox_copy exceeds the total byte limit")
                digest.update(chunk)
                _write_all(output, chunk)
            captured_mode = stat.S_IMODE(before.st_mode) if self.preserve_modes else 0
            frozen_mode = 0o400 | (0o100 if self.preserve_modes and captured_mode & 0o111 else 0)
            os.fchmod(output, frozen_mode)

View on GitHub (pinned to d540b00184)

Solutions

  1. Reduce the declared sandbox_copy set so total bytes stay well under 2 GiB; the realistic ceiling is the ~428 MiB index plus modest extras.
  2. Identify the largest files with `du -ah <declared-root> | sort -rh | head` and exclude generated/vendored blobs.
  3. Split the task so the heaviest asset is mounted as a sandbox_dependency (read-only bind) rather than copied into the snapshot, if applicable.
  4. If the large file is generated, add it to .gitignore and regenerate it inside the arm clone rather than capturing it.

Example fix

// before
{"sandbox_copy": ["repo/.gitnexus/index", "repo/coverage-report", "repo/test-fixtures/large-dataset.bin"]}

// after — drop the dataset, keep it out of the snapshot
{"sandbox_copy": ["repo/.gitnexus/index"]}
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path
import os

MAX_BYTES = 2 * 1024 * 1024 * 1024

def total_declared_bytes(repo: Path, declarations: list[str]) -> int:
    total = 0
    for raw in declarations:
        root = repo / raw
        for current, _, files in os.walk(root, followlinks=False):
            for name in files:
                p = Path(current) / name
                st = p.lstat()
                if (st.st_mode & 0o170000) == 0o100000:  # S_ISREG
                    total += st.st_size
                    if total > MAX_BYTES:
                        return total
    return total

# run before prepare
total = total_declared_bytes(repo_path, task["sandbox_copy"])
if total > MAX_BYTES:
    raise ValueError(f"sandbox_copy declares {total} bytes, limit is {MAX_BYTES}")

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 "exceeds the total byte limit" in str(exc):
        # largest offenders:
        #   du -ah <declared> | sort -rh | head
        raise
    raise

Prevention

When it happens

Trigger: A single declared file whose st_size, added to the running budget, crosses 2*1024*1024*1024 bytes. Common when the shipped GitNexus index (~428 MiB) is declared alongside other large inputs, or when a task accidentally declares a huge generated artifact (coverage dump, core file, packed dataset) as sandbox_copy.

Common situations: Declaring `node_modules` plus a large index plus a coverage tarball in one task. Pointing sandbox_copy at a repo root that contains a multi-gigabyte `.git/objects/pack` or a vendored dataset. Misconfigured task that copies an entire build output tree rather than a slice.

Related errors


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