abhigyanpatwari/GitNexus · error · SandboxError

sandbox_copy must be a list of nonblank repository-relative

Error message

sandbox_copy must be a list of nonblank repository-relative paths

What it means

Raised by _sandbox_copy_declarations when the task's `sandbox_copy` field is absent-as-non-list or is a list containing any element that is not a non-blank string. The field must be a list of repository-relative path strings, each truthy (non-empty after strip semantics of truthiness). This is the top-level schema gate for sandbox_copy declarations, evaluated before any path validation.

Source

Thrown at eval/workflow_bench/task_assets.py:545

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

        Used for harness-owned mount points that must exist in the captured
        bytes rather than be created against a read-only bind at runtime.
        """

        self._record_directory(relative)

    def finished_entries(self) -> tuple[AssetManifestEntry, ...]:
        return tuple(sorted(self.entries.values(), key=lambda entry: entry.path.as_posix()))


def _sandbox_copy_declarations(
    task: Mapping[str, Any],
) -> tuple[tuple[str, ...], tuple[PurePosixPath, ...]]:
    raw_declarations = task.get("sandbox_copy", [])
    if not isinstance(raw_declarations, list) or not all(isinstance(item, str) and item for item in raw_declarations):
        raise SandboxError("sandbox_copy must be a list of nonblank repository-relative paths")
    declarations = tuple(raw_declarations)
    paths: list[PurePosixPath] = []
    for raw in declarations:
        relative = PurePosixPath(raw)
        if relative.is_absolute() or not relative.parts or ".." in relative.parts:
            raise SandboxError(f"sandbox_copy must be a repository-relative path: {raw!r}")
        _validate_manifest_path(relative)
        paths.append(relative)
    for index, path in enumerate(paths):
        for other in paths[index + 1 :]:
            if path == other or path in other.parents or other in path.parents:
                raise SandboxError(f"sandbox_copy declarations overlap: {path} and {other}")
    return declarations, tuple(paths)


def _sandbox_dependency_declarations(
    task: Mapping[str, Any],
) -> tuple[_DependencyDeclaration, ...]:

View on GitHub (pinned to d540b00184)

Solutions

  1. Ensure `sandbox_copy` is a JSON list of non-empty strings: `["path/one", "path/two"]`.
  2. Validate the task document against the expected schema before passing it to prepare() (see validationCode).
  3. Omit the field entirely (`sandbox_copy: []` or absent) if no paths are needed — both are accepted.
  4. Strip whitespace and drop blanks at authoring time so no empty-string element reaches the harness.

Example fix

// before
{"sandbox_copy": "repo/src"}
// or
{"sandbox_copy": ["repo/src", "", null]}

// after
{"sandbox_copy": ["repo/src"]}
Defensive patterns

Strategy: type-guard

Validate before calling

def validate_sandbox_copy_shape(task: dict) -> None:
    raw = task.get("sandbox_copy", [])
    if not isinstance(raw, list):
        raise ValueError("sandbox_copy must be a list")
    for item in raw:
        if not isinstance(item, str) or not item:
            raise ValueError(f"sandbox_copy entries must be nonblank strings, got {item!r}")

validate_sandbox_copy_shape(task)

Type guard

from typing import Any

def is_sandbox_copy_list(value: Any) -> bool:
    return (
        isinstance(value, list)
        and all(isinstance(item, str) and item for item in value)
    )

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 "must be a list of nonblank" in str(exc):
        # fix the sandbox_copy field shape in the task document
        raise
    raise

Prevention

When it happens

Trigger: Task JSON has `"sandbox_copy": "src"` (a string, not a list), `"sandbox_copy": ["src", 123]` (mixed types), `"sandbox_copy": ["src", ""]` (blank element), `"sandbox_copy": ["src", null]`, or `"sandbox_copy": {"paths": [...]}` (a dict). The check is `isinstance(item, str) and item` for every element.

Common situations: Hand-writing task YAML/JSON and forgetting the list brackets. Copying a single-path shorthand from another tool that accepts a bare string. A serialization bug that emits None or empty strings for missing optional paths. Schema drift between task authoring and the harness contract.

Related errors


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