abhigyanpatwari/GitNexus · error · SandboxError
dependency symlink changed while snapshotting: {relative}
Error message
dependency symlink changed while snapshotting: {relative} What it means
Raised by _copy_symlink after creating the destination link: if a re-stat of the source link shows a different mutation identity (dev, ino, mode, size, mtime_ns, ctime_ns) or a second readlink returns a different target string, the snapshot is rejected as inconsistent. This is the symlink analogue of the file-changed TOCTOU guard and guarantees the captured link target matches what was on disk at the recorded moment.
Source
Thrown at eval/workflow_bench/task_assets.py:490
raise SandboxError(f"dependency symlink is unreadable or not UTF-8: {relative}") from exc
if not target or PurePosixPath(target).is_absolute() or "\x00" in target:
raise SandboxError(f"dependency symlink must be a bounded relative link: {relative}")
if len(target_bytes) > MAX_TASK_ASSET_PATH_BYTES:
raise SandboxError(f"dependency symlink target exceeds the path limit: {relative}")
if self.budget.total_bytes + len(target_bytes) > MAX_TASK_ASSET_BYTES:
raise SandboxError("sandbox_copy exceeds the total byte limit")
destination = self.destination / Path(*relative.parts)
os.symlink(target, destination)
after = os.stat(name, dir_fd=parent_descriptor, follow_symlinks=False)
if (
_mutation_identity(before) != _mutation_identity(after)
or os.readlink(
name,
dir_fd=parent_descriptor,
)
!= target
):
raise SandboxError(f"dependency symlink changed while snapshotting: {relative}")
self.total_bytes += len(target_bytes)
self.budget.total_bytes += len(target_bytes)
self._record(
AssetManifestEntry(
path=relative,
kind="symlink",
size=len(target_bytes),
sha256=hashlib.sha256(target_bytes).hexdigest(),
link_target=target,
)
)
def _ensure_parents(self, relative: PurePosixPath) -> None:
current = PurePosixPath()
for part in relative.parts:
current /= part
existing = self.entries.get(current)
if existing is not None:View on GitHub (pinned to d540b00184)
Solutions
- Quiesce the tree before capture: stop package managers, watch modes, and any process that may touch node_modules.
- Capture against a clean checkout — `git worktree add` at the resolved SHA and install dependencies into that isolated tree before snapshotting.
- Re-run after the tree is stable; this is a transient race.
- If reproducible, identify which process rewrites the link and sequence it out of the capture window.
Example fix
# before — capturing while pnpm deduplicates pnpm install & snapshot = cache.prepare(task, repo=repo, resolved_sha=sha) # after — finish install, then capture pnpm install wait snapshot = cache.prepare(task, repo=repo, resolved_sha=sha)
Defensive patterns
Strategy: validation
Validate before calling
from pathlib import Path
import os, time
def assert_dep_symlinks_stable(dep_source: Path, gap: float = 0.5) -> None:
def fp():
out = {}
for current, dirs, files in os.walk(dep_source, followlinks=False):
for name in dirs + files:
p = Path(current) / name
if p.is_symlink():
st = p.lstat()
out[str(p)] = (st.st_ino, st.st_mtime_ns, os.readlink(p))
return out
a = fp(); time.sleep(gap); b = fp()
if a != b:
raise ValueError(f"dependency symlinks not stable: {[k for k in a if a[k]!=b.get(k)][:5]}")
for d in task.get("sandbox_dependencies", []):
assert_dep_symlinks_stable(repo_path / d["source"]) 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 "changed while snapshotting" in str(exc):
# stop package managers / watch modes, then retry once
raise
raise Prevention
- Finish all package-manager operations (npm/pnpm/yarn install) before capture.
- Stop watch modes that touch node_modules (vite, nodemon, webpack-dev-server).
- Capture against an isolated git worktree so no concurrent process mutates links.
When it happens
Trigger: The symlink is deleted and recreated (changing ino), retargeted (`ln -sfn` by another process), or its metadata changed (chmod) between the initial stat (passed as `before`) and the post-copy verification. Most common when a package manager or build tool rewrites node_modules links concurrently with snapshot capture.
Common situations: npm/pnpm/yarn reinstalling or deduplicating links while the benchmark captures a dependency snapshot. A watch mode tool (vite, nodemon) touching node_modules. Running snapshot capture against a working tree another process is mutating.
Related errors
- sandbox_copy file changed while snapshotting: {relative}
- oracle stage parent changed during verification: {item.targe
- dependency snapshot changed: {dependency.source}
- dependency symlink is unreadable or not UTF-8: {relative}
- dependency symlink must be a bounded relative link: {relativ
AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12).
Data as JSON: /api/errors/de73daadca601cb2.
Report an issue: GitHub.