abhigyanpatwari/GitNexus · error · SandboxError

task asset cache is already closed

Error message

task asset cache is already closed

What it means

TaskAssetCache.prepare refuses to run after close() (or context-manager __exit__). The cache root has been deleted, so any new snapshot would be written into a removed tree.

Source

Thrown at eval/workflow_bench/task_assets.py:216

    def __enter__(self) -> TaskAssetCache:
        return self

    def __exit__(self, _exc_type: object, _exc: object, _traceback: object) -> None:
        self.close()

    def prepare(
        self,
        task: Mapping[str, Any],
        *,
        repo: Path,
        resolved_sha: str,
        expected_dependency_binding: Mapping[str, Any] | None = None,
    ) -> TaskAssetSnapshot:
        """Capture or reuse all copied and mounted task bytes in one snapshot."""

        if self._closed:
            raise SandboxError("task asset cache is already closed")
        repo_identity = _real_directory(repo, label="task asset repository")
        declarations, relative_paths = _sandbox_copy_declarations(task)
        dependency_declarations = _sandbox_dependency_declarations(task)
        dependency_identity = tuple((declaration.source, declaration.target) for declaration in dependency_declarations)
        definition = (str(repo_identity), resolved_sha, declarations, dependency_identity)
        existing = self._by_definition.get(definition)
        if existing is not None:
            if expected_dependency_binding is not None:
                existing.validate_dependency_binding(expected_dependency_binding)
            return existing

        building = Path(tempfile.mkdtemp(prefix=".building-", dir=self.root))
        try:
            copy_root = building / "sandbox-copy"
            dependency_root = building / "dependencies"
            copy_root.mkdir(mode=0o700)
            dependency_root.mkdir(mode=0o700)
            budget = _SnapshotBudget()

View on GitHub (pinned to d540b00184)

Solutions

  1. Move all cache.prepare / materialize / dependency_mounts usage inside the 'with TaskAssetCache(...) as cache:' block.
  2. If you need the cache longer, widen the with scope or delay close() until every arm has consumed the snapshot.
  3. Do not retain or reuse a reference to a cache after close().

Example fix

# before
with TaskAssetCache(root) as cache:
    snapshot = cache.prepare(task, repo=repo, resolved_sha=sha)
# later, outside the with-block:
cache.prepare(other_task, repo=repo, resolved_sha=sha)   # raises: cache closed

# after
with TaskAssetCache(root) as cache:
    snapshot = cache.prepare(task, repo=repo, resolved_sha=sha)
    other = cache.prepare(other_task, repo=repo, resolved_sha=sha)
snapshot.materialize(clone)   # snapshot survives the cache
Defensive patterns

Strategy: validation

Validate before calling

def assert_cache_open(cache):
    if getattr(cache, "_closed", True):
        raise RuntimeError("TaskAssetCache is closed; call prepare within its with-block")

Type guard

def is_cache_open(cache) -> bool:
    return not getattr(cache, "_closed", True)

Prevention

When it happens

Trigger: Calling cache.prepare(...) after the 'with TaskAssetCache(...)' block exited, or after an explicit cache.close().

Common situations: A refactor that moved the prepare call outside the with block; reusing a closed cache across functions; holding a stale cache reference.

Related errors


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