abhigyanpatwari/GitNexus · error · SandboxError

sandbox_copy paths collide at {current}

Error message

sandbox_copy paths collide at {current}

What it means

Raised by _SnapshotBuilder._ensure_parents while walking the parent components of a path being recorded: if an intermediate path component was already recorded in the manifest as a non-directory entry (a file or symlink), the tree is inconsistent because a file cannot have children. This catches manifest-shape conflicts where a declared file path collides with a directory prefix of another declared path.

Source

Thrown at eval/workflow_bench/task_assets.py:510

        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:
                if existing.kind != "directory":
                    raise SandboxError(f"sandbox_copy paths collide at {current}")
                continue
            self._record(AssetManifestEntry(path=current, kind="directory"))
            (self.destination / Path(*current.parts)).mkdir(mode=0o700, exist_ok=True)

    def _record(self, entry: AssetManifestEntry) -> None:
        _validate_manifest_path(entry.path)
        existing = self.entries.get(entry.path)
        if existing is not None:
            if existing != entry:
                raise SandboxError(f"sandbox_copy paths collide at {entry.path}")
            return
        if self.budget.entries >= MAX_TASK_ASSET_ENTRIES:
            raise SandboxError("sandbox_copy exceeds the entry limit")
        self.entries[entry.path] = entry
        self.budget.entries += 1

    def ensure_directory(self, relative: PurePosixPath) -> None:
        """Record and create one extra directory inside this snapshot.

View on GitHub (pinned to d540b00184)

Solutions

  1. Inspect the declared paths and confirm none is simultaneously a file and a directory prefix: review `sandbox_copy` and `sandbox_dependencies` source/target pairs.
  2. Ensure the repo tree is consistent — `build` should not be a file in one checkout and a directory in another; align the checked-out revision.
  3. Narrow declarations so file and directory paths do not shadow each other.
  4. Re-run after `git clean -fdx` to remove stray files that shadow intended directories.

Example fix

// before — inconsistent tree: 'config' is a file, but a dep needs 'config/x'
{"sandbox_copy": ["repo/config"],
 "sandbox_dependencies": [{"source": "overlay/config/x", "target": "config/x"}]}

// after — rename so paths don't collide
mv repo/config repo/config.json
{"sandbox_copy": ["repo/config.json"]}
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path, PurePosixPath

def validate_no_path_shadowing(declared_paths: list[PurePosixPath]) -> None:
    """Reject if any path is both a file and a directory-prefix of another."""
    s = sorted(set(declared_paths))
    for i, a in enumerate(s):
        for b in s[i+1:]:
            if a == b or b in a.parents or a in b.parents:
                raise ValueError(f"paths collide (nest/duplicate): {a} and {b}")

validate_no_path_shadowing([PurePosixPath(p) for p in task["sandbox_copy"]])

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 "paths collide" in str(exc):
        # inspect declared paths for a file vs directory-prefix conflict
        raise
    raise

Prevention

When it happens

Trigger: Two sandbox_copy or dependency declarations where one names a file at path P and another names something under P/ (e.g. declaring both `repo/foo` as a file and `repo/foo/bar`). Also possible if a directory was recorded as a file due to a race, though the path-collision semantics are the primary trigger.

Common situations: A task declares `sandbox_copy: ["repo/build"]` where `build` is a regular file in one arm and a directory in another. Overlapping declarations that slipped past the overlap check because one side is a file. A symlink in the dependency tree (allow_symlinks=True) recorded at a path that another entry tries to treat as a directory parent.

Related errors


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