abhigyanpatwari/GitNexus · error · SandboxError
sandbox_dependencies require a caller-owned immutable task a
Error message
sandbox_dependencies require a caller-owned immutable task asset snapshot
What it means
Raised by stage_task_assets when snapshot is None but the task declares non-empty sandbox_dependencies. Dependency mounts require a caller-owned, immutable TaskAssetSnapshot (captured once by the benchmark runner and reused across arms) so that every arm sees identical, frozen dependency bytes; an ad-hoc ephemeral capture inside stage_task_assets cannot provide that guarantee for dependencies, so the call is rejected rather than silently doing the wrong thing.
Source
Thrown at eval/workflow_bench/task_assets.py:1080
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 and pass a snapshot: with TaskAssetCache(cache_dir) as cache: snapshot = cache.prepare(task, repo=repo, resolved_sha=sha); then stage_task_assets(task, repo=repo, clone=clone, snapshot=snapshot).
- Use capture_task_dependency_binding first if you only need the dependency binding digests, then prepare the full snapshot for staging.
- If you do not need dependencies, remove the sandbox_dependencies key from the task so the ephemeral-snapshot path applies.
Example fix
# before: dependencies declared but no snapshot supplied
stage_task_assets(
{'sandbox_copy': [...], 'sandbox_dependencies': [{'source': 'x', 'target': 'y'}]},
repo=repo, clone=clone,
) # -> raises
# after: prepare a caller-owned snapshot and pass it through
with TaskAssetCache(cache_dir) as cache:
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_dependency_declarations, TaskAssetCache
def stage_safely(task, *, repo, clone, cache_dir, resolved_sha):
if _sandbox_dependency_declarations(task):
with TaskAssetCache(cache_dir) as cache:
snapshot = cache.prepare(task, repo=repo, resolved_sha=resolved_sha)
else:
snapshot = None # sandbox_copy-only: ephemeral snapshot path is fine
return stage_task_assets(task, repo=repo, clone=clone, snapshot=snapshot)
# Always build a caller-owned snapshot when the task declares sandbox_dependencies. Type guard
def needs_caller_snapshot(task) -> bool:
return bool(_sandbox_dependency_declarations(task)) Try / catch
from .proposer_sandbox import SandboxError
try:
stage_task_assets(task, repo=repo, clone=clone) # no snapshot
except SandboxError as exc:
if 'require a caller-owned immutable task asset snapshot' in str(exc):
with TaskAssetCache(cache_dir) as cache:
snapshot = cache.prepare(task, repo=repo, resolved_sha=sha)
stage_task_assets(task, repo=repo, clone=clone, snapshot=snapshot)
else:
raise Prevention
- Always plumb a caller-owned snapshot through when the task declares sandbox_dependencies.
- Use capture_task_dependency_binding + cache.prepare in the benchmark runner; do not call stage_task_assets bare for dependency tasks.
- Drop sandbox_dependencies from the task if you only need sandbox_copy and want the ephemeral path.
When it happens
Trigger: Calling stage_task_assets(task, repo=..., clone=...) without a snapshot argument on a task whose sandbox_dependencies list is non-empty. The fallback ephemeral-snapshot branch (for sandbox_copy-only standalone callers) does not support dependencies.
Common situations: Standalone/containment test calling stage_task_assets directly without first building a snapshot via TaskAssetCache; refactoring a caller to use dependencies but forgetting to plumb the snapshot through; benchmark runner path bypassed by an external script.
Related errors
- sandbox_dependencies must be a list
- sandbox_dependencies entries require only nonblank source an
- dependency source must stay inside the repository: {source_p
- dependency target must stay inside the clone: {target_path}
- sandbox dependency targets overlap: {declaration.target} and
AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12).
Data as JSON: /api/errors/6c8fd1447d34b591.
Report an issue: GitHub.