abhigyanpatwari/GitNexus · error · SandboxError

dependency target must stay inside the clone: {target_path}

Error message

dependency target must stay inside the clone: {target_path}

What it means

Raised by _sandbox_dependency_declarations when a dependency's `target` path is absolute, contains '..', or is empty. The target is a clone-relative mount point (where the dependency appears inside the arm clone under SANDBOX_WORKSPACE), so it must stay inside the clone. An escaping target would mount bytes outside the sandbox workspace, breaking containment. Note the asymmetry: source is repo-relative, target is clone-relative — both must be bounded relative paths.

Source

Thrown at eval/workflow_bench/task_assets.py:582

    raw_declarations = task.get("sandbox_dependencies", [])
    if not isinstance(raw_declarations, list):
        raise SandboxError("sandbox_dependencies must be a list")
    declarations: list[_DependencyDeclaration] = []
    for item in raw_declarations:
        if (
            not isinstance(item, Mapping)
            or set(item) != {"source", "target"}
            or not all(isinstance(item[field], str) and item[field] for field in ("source", "target"))
        ):
            raise SandboxError("sandbox_dependencies entries require only nonblank source and target")
        source = str(item["source"])
        target = str(item["target"])
        source_path = PurePosixPath(source)
        target_path = PurePosixPath(target)
        if source_path.is_absolute() or ".." in source_path.parts or not source_path.parts:
            raise SandboxError(f"dependency source must stay inside the repository: {source_path}")
        if target_path.is_absolute() or ".." in target_path.parts or not target_path.parts:
            raise SandboxError(f"dependency target must stay inside the clone: {target_path}")
        _validate_manifest_path(source_path)
        _validate_manifest_path(target_path)
        declarations.append(
            _DependencyDeclaration(
                source=source,
                target=target,
                source_path=source_path,
                target_path=target_path,
            )
        )
    for index, declaration in enumerate(declarations):
        for other in declarations[index + 1 :]:
            if (
                declaration.target_path == other.target_path
                or declaration.target_path in other.target_path.parents
                or other.target_path in declaration.target_path.parents
            ):
                raise SandboxError(f"sandbox dependency targets overlap: {declaration.target} and {other.target}")

View on GitHub (pinned to d540b00184)

Solutions

  1. Rewrite each dependency target as a clean clone-relative path with no '..': `node_modules`, `vendor`, not `/mnt/dep` or `../outside`.
  2. Remember the target is relative to the arm clone's SANDBOX_WORKSPACE, not the host filesystem.
  3. Avoid targets that resolve to or above the workspace root.
  4. Validate with the provided path guard before prepare() (see validationCode).

Example fix

// before
{"sandbox_dependencies": [
  {"source": "node_modules", "target": "/arm/node_modules"}
]}

// after
{"sandbox_dependencies": [
  {"source": "node_modules", "target": "node_modules"}
]}
Defensive patterns

Strategy: type-guard

Validate before calling

from pathlib import PurePosixPath

def validate_dependency_targets_relative(task: dict) -> None:
    for d in task.get("sandbox_dependencies", []):
        tp = PurePosixPath(d["target"])
        if tp.is_absolute() or ".." in tp.parts or not tp.parts:
            raise ValueError(f"dependency target must stay inside clone: {tp}")

validate_dependency_targets_relative(task)

Type guard

from pathlib import PurePosixPath

def dependency_target_is_bounded(raw: str) -> bool:
    p = PurePosixPath(raw)
    return not p.is_absolute() and bool(p.parts) and ".." not in p.parts

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 "target must stay inside the clone" in str(exc):
        # rewrite the target as a clone-relative path
        raise
    raise

Prevention

When it happens

Trigger: A dependency target like `/abs/mount`, `../escape`, `node_modules/../../../`, or `''`. Also a target that resolves to the clone root itself (empty parts). The check mirrors the source check: `target_path.is_absolute() or '..' in target_path.parts or not target_path.parts`.

Common situations: Author uses an absolute mount path. Confusing source and target semantics and using an absolute path for target. A templating bug that injects '..'. Mounting to a parent of the workspace.

Related errors


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