abhigyanpatwari/GitNexus · critical · SandboxError
task asset snapshot file changed: {entry.path}
Error message
task asset snapshot file changed: {entry.path} What it means
_materialize_file first lstat's the source snapshot file and requires: not a symlink, is a regular file, and st_size equals entry.size from the manifest. Any deviation aborts — the snapshot must be byte-identical to what was captured. Indicates the captured snapshot file was replaced, truncated, extended, or symlinked between capture and materialize, or that entry.size in the manifest is stale.
Source
Thrown at eval/workflow_bench/task_assets.py:809
relative.name,
src_dir_fd=source_parent,
dst_dir_fd=destination_parent,
)
finally:
os.close(destination_parent)
os.close(source_parent)
def _materialize_file(
source: Path,
destination: Path,
entry: AssetManifestEntry,
*,
fallback_budget: int,
) -> int:
metadata = source.lstat()
if stat.S_ISLNK(metadata.st_mode) or not stat.S_ISREG(metadata.st_mode) or metadata.st_size != entry.size:
raise SandboxError(f"task asset snapshot file changed: {entry.path}")
temporary = destination.with_name(f".{destination.name}.{uuid.uuid4().hex}.tmp")
source_descriptor = os.open(source, os.O_RDONLY | getattr(os, "O_CLOEXEC", 0) | getattr(os, "O_NOFOLLOW", 0))
destination_descriptor = os.open(
temporary,
os.O_WRONLY | os.O_CREAT | os.O_EXCL | getattr(os, "O_CLOEXEC", 0),
0o600,
)
fallback_bytes = 0
try:
opened = os.fstat(source_descriptor)
if _mutation_identity(opened) != _mutation_identity(metadata):
raise SandboxError(f"task asset snapshot file changed: {entry.path}")
if _try_reflink(source_descriptor, destination_descriptor):
if os.fstat(destination_descriptor).st_size != entry.size:
raise SandboxError(f"task asset reflink produced an invalid file: {entry.path}")
else:
if entry.size > fallback_budget:
raise SandboxError(View on GitHub (pinned to d540b00184)
Solutions
- Confirm nothing writes to the TaskAssetCache.root directory during or between runs
- Delete the cache entry and re-prepare the snapshot from scratch
- Verify entry.size matches os.lstat(source).st_size to detect a stale manifest
Defensive patterns
Strategy: validation
Validate before calling
import os, stat
from pathlib import Path
def snapshot_files_match_manifest(snapshot_root: Path, entries) -> list[str]:
bad = []
root = snapshot_root / "sandbox-copy"
for e in entries:
if e.get("kind") != "file":
continue
p = root / Path(*PurePosixPath(e["path"]).parts)
try:
m = p.lstat()
except OSError as exc:
bad.append(f"{e['path']}: {exc}"); continue
if stat.S_ISLNK(m.st_mode) or not stat.S_ISREG(m.st_mode) or m.st_size != e["size"]:
bad.append(e["path"])
return bad Try / catch
from eval.workflow_bench.proposer_sandbox import SandboxError
try:
snapshot.materialize(clone)
except SandboxError as exc:
if "snapshot file changed" in str(exc):
raise SystemExit(f"snapshot mutated since capture; delete the cache entry and re-prepare: {exc}") from exc
raise Prevention
- Treat the TaskAssetCache.root as immutable once written; never let a second writer touch it
- Disable backup and AV scanners on the cache directory
- Validate snapshot files against the manifest size before materialize
When it happens
Trigger: The captured snapshot file was mutated after capture; the manifest size field does not match the on-disk file; the snapshot root was replaced or corrupted.
Common situations: Two arms sharing the snapshot root with one mutating it (the snapshot is meant to be immutable); disk corruption; an external process (backup, AV) touching the cache dir.
Related errors
- task asset snapshot file changed while materializing: {entry
- dependency snapshot changed: {dependency.source}
- sandbox_copy file changed while snapshotting: {relative}
- dependency symlink changed while snapshotting: {relative}
- task asset snapshot does not match this task declaration
AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12).
Data as JSON: /api/errors/bf9c37e3fb764dba.
Report an issue: GitHub.