abhigyanpatwari/GitNexus · error · SandboxError
task asset snapshot does not match this task declaration
Error message
task asset snapshot does not match this task declaration
What it means
Raised by stage_task_assets when a supplied snapshot's repo_identity (real directory path of the repo) or sandbox_copy declarations do not match the current task/repo. The snapshot is provenance-bound: it must have been captured from the same real repo directory and the same sandbox_copy declaration set, otherwise its frozen bytes cannot be trusted to represent this task's inputs.
Source
Thrown at eval/workflow_bench/task_assets.py:1075
def stage_task_assets(
task: Mapping[str, Any],
*,
repo: Path,
clone: Path,
snapshot: TaskAssetSnapshot | None = None,
) -> list[ReadOnlyMount]:
"""Materialize copied assets and validate read-only dependency mounts.
``snapshot`` is supplied by the benchmark runner so every arm reuses one
capture. The optional path preserves the historic standalone helper API
for containment tests and external callers.
"""
repo_identity = _real_directory(repo, label="task asset repository")
declarations, _ = _sandbox_copy_declarations(task)
if snapshot is not None:
if snapshot.repo_identity != repo_identity or snapshot.declarations != declarations:
raise SandboxError("task asset snapshot does not match this task declaration")
snapshot.materialize(clone)
return _dependency_mounts(task, clone=clone, snapshot=snapshot)
if _sandbox_dependency_declarations(task):
raise SandboxError("sandbox_dependencies require a caller-owned immutable task asset snapshot")
with tempfile.TemporaryDirectory(prefix="wfbench-asset-snapshot-") as temporary:
with TaskAssetCache(Path(temporary) / "cache") as cache:
ephemeral = cache.prepare(task, repo=repo_identity, resolved_sha="unbound")
ephemeral.materialize(clone)
return []
View on GitHub (pinned to d540b00184)
Solutions
- Build the snapshot and stage from it in the same run, passing the identical repo Path to both cache.prepare and stage_task_assets (resolve symlinks consistently — _real_directory is used both sides).
- After editing sandbox_copy in a task, call cache.prepare again to capture a fresh snapshot; do not reuse the old one.
- Key any snapshot cache by (repo_identity, resolved_sha, sandbox_copy declarations, sandbox_dependencies) and discard entries that no longer match.
Example fix
# before: snapshot built once, reused after sandbox_copy changed
snapshot = cache.prepare(task, repo=repo, resolved_sha=sha)
task['sandbox_copy'].append('build/out') # declaration mutated
stage_task_assets(task, repo=repo, clone=clone, snapshot=snapshot) # -> mismatch
# after: rebuild the snapshot whenever the task declaration changes
task['sandbox_copy'].append('build/out')
snapshot = cache.prepare(task, repo=repo, resolved_sha=sha)
stage_task_assets(task, repo=repo, clone=clone, snapshot=snapshot) Defensive patterns
Strategy: validation
Validate before calling
from .task_assets import _sandbox_copy_declarations, _real_directory
def assert_snapshot_matches_task(task, *, repo, snapshot) -> None:
repo_identity = _real_directory(repo, label='task asset repository')
declarations, _ = _sandbox_copy_declarations(task)
if snapshot.repo_identity != repo_identity or snapshot.declarations != declarations:
raise ValueError(
'snapshot provenance does not match task/repo; rebuild via cache.prepare.'
)
# Call before stage_task_assets; rebuild if mismatched. Type guard
def snapshot_matches_task(task, *, repo, snapshot) -> bool:
repo_identity = _real_directory(repo, label='task asset repository')
declarations, _ = _sandbox_copy_declarations(task)
return snapshot.repo_identity == repo_identity and snapshot.declarations == declarations Try / catch
from .proposer_sandbox import SandboxError
try:
stage_task_assets(task, repo=repo, clone=clone, snapshot=snapshot)
except SandboxError as exc:
if 'does not match this task declaration' in str(exc):
with TaskAssetCache(cache_dir) as cache:
snapshot = cache.prepare(task, repo=_real_directory(repo, label='task asset repository'), resolved_sha=sha)
stage_task_assets(task, repo=repo, clone=clone, snapshot=snapshot)
else:
raise Prevention
- Resolve repo consistently (same real path) for both cache.prepare and stage_task_assets.
- Rebuild the snapshot whenever sandbox_copy or the repo workdir changes.
- Key snapshot caches by (repo_identity, resolved_sha, sandbox_copy declarations, sandbox_dependencies).
When it happens
Trigger: Passing a snapshot built from repo A into a stage call for repo B; reusing a snapshot after the sandbox_copy declaration was edited; passing a snapshot whose repo_identity was resolved via a symlink while stage resolves a different real path (or vice versa); snapshot built with resolved_sha='unbound' for a task that now needs a bound snapshot.
Common situations: Caching a snapshot across CI runs and reusing it after the repo path changed (different workdir); editing sandbox_copy between prepare and stage; resolving repo via a symlink in one path and the realpath in another; mixing the ephemeral standalone snapshot path with the benchmark runner's caller-owned snapshot.
Related errors
- sandbox_copy paths collide at {entry.path}
- task asset snapshot file changed: {entry.path}
- task asset snapshot file changed while materializing: {entry
- task asset snapshot does not match this dependency declarati
- {label} is unavailable: {path}: {exc}
AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12).
Data as JSON: /api/errors/a9b2aa921cc9c8d8.
Report an issue: GitHub.