abhigyanpatwari/GitNexus · error · SandboxError

sandbox dependency content changed after task binding

Error message

sandbox dependency content changed after task binding

What it means

Raised by _validate_dependency_binding_values when the sandbox_dependency_content_digest / sandbox_dependency_manifest_digest read from a task binding do not equal the freshly computed digests of the current dependency snapshot. Bindings pin the exact dependency bytes; any mismatch means dependency content drifted after the binding was captured, so running the arm would use different inputs than were recorded.

Source

Thrown at eval/workflow_bench/task_assets.py:961

    manifest_digest = hashlib.sha256(
        json.dumps(manifest_payload, sort_keys=True, separators=(",", ":")).encode()
    ).hexdigest()
    return content_digest, manifest_digest


def _validate_dependency_binding_values(
    binding: Mapping[str, Any],
    *,
    content_digest: str,
    manifest_digest: str,
) -> None:
    expected = {
        DEPENDENCY_CONTENT_BINDING_FIELD: content_digest,
        DEPENDENCY_MANIFEST_BINDING_FIELD: manifest_digest,
    }
    supplied = {field: binding.get(field) for field in expected}
    if supplied != expected:
        raise SandboxError("sandbox dependency content changed after task binding")


def _snapshot_digest(
    *,
    repo_identity: Path,
    resolved_sha: str,
    declarations: tuple[str, ...],
    manifest_digest: str,
    dependency_content_digest: str,
    dependency_manifest_digest: str,
) -> str:
    payload = {
        "declarations": declarations,
        "dependency_content_digest": dependency_content_digest,
        "dependency_manifest_digest": dependency_manifest_digest,
        "manifest_digest": manifest_digest,
        "repo_identity": str(repo_identity),
        "resolved_sha": resolved_sha,

View on GitHub (pinned to d540b00184)

Solutions

  1. Re-capture the binding immediately before staging assets: call capture_task_dependency_binding(task, repo=repo, resolved_sha=sha) and store its result, then pass the same dict to validate_dependency_binding.
  2. Ensure the dependency source tree is read-only / at a fixed resolved_sha between capture and validation (checkout the pinned sha in both steps).
  3. If you serialized the binding, verify the keys sandbox_dependency_content_digest and sandbox_dependency_manifest_digest round-trip unchanged (no float coercion, no truncation).

Example fix

# before: binding captured once, repo later moved
binding = capture_task_dependency_binding(task, repo=repo, resolved_sha=old_sha)
git_checkout('main')              # dependency bytes changed
snapshot.validate_dependency_binding(binding)   # -> mismatch

# after: capture and validate at the same sha
snapshot = cache.prepare(task, repo=repo, resolved_sha=sha)
binding = snapshot.dependency_binding
# ... no repo mutation in between ...
snapshot.validate_dependency_binding(binding)
Defensive patterns

Strategy: validation

Validate before calling

from .task_assets import capture_task_dependency_binding, TaskAssetCache

def fresh_binding(task, *, repo, resolved_sha) -> dict[str, str]:
    # Always (re)capture at the exact sha you will run against.
    return capture_task_dependency_binding(task, repo=repo, resolved_sha=resolved_sha)

# Compare against any stored binding *before* staging:
expected = snapshot.dependency_binding
if stored != expected:
    raise ValueError('stored dependency binding is stale; recapture before staging')

Type guard

def binding_matches(binding: Mapping[str, object], snapshot: 'TaskAssetSnapshot') -> bool:
    return binding == snapshot.dependency_binding

Try / catch

from .proposer_sandbox import SandboxError

try:
    snapshot.validate_dependency_binding(binding)
except SandboxError as exc:
    if 'dependency content changed' in str(exc):
        # Re-capture at the current sha and update the stored binding; do NOT
        # edit the binding by hand.
        binding = capture_task_dependency_binding(task, repo=repo, resolved_sha=sha)
        snapshot.validate_dependency_binding(binding)
    else:
        raise

Prevention

When it happens

Trigger: capture_task_dependency_binding recorded digests at one point, then the dependency source files changed (commit, checkout, rebuild, manual edit), then validate_dependency_binding was called against the new bytes. Also fires if the binding dict was hand-edited, truncated, or loaded from a stale/serialized form.

Common situations: git checkout between binding capture and arm run changing dependency files; a rebuild of the shipped index; two branches with different dependency trees sharing a binding file; serialization that dropped or renamed the binding keys.

Related errors


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