abhigyanpatwari/GitNexus · error · SandboxError

results artifact parent must be a real directory: {current}

Error message

results artifact parent must be a real directory: {current}

What it means

In the same parent walk, if a parent component's lstat mode is a symlink or not a directory, the harness refuses it. This blocks a path component that looks like a directory in results.jsonl but is actually a file or a symlink — a classic traversal/swap vehicle.

Source

Thrown at eval/workflow_bench/evolve.py:319

def _results_artifact_path(root: Path, relative_value: str, *, transcript: bool) -> Path:
    relative = PurePosixPath(relative_value)
    expected_parts = 2 if transcript else 1
    if (
        relative.is_absolute()
        or len(relative.parts) != expected_parts
        or any(part in {"", ".", ".."} for part in relative.parts)
        or (transcript and relative.parts[0] != "transcripts")
    ):
        raise SandboxError(f"unsafe results artifact path: {relative_value!r}")
    current = root
    for part in relative.parts[:-1]:
        current /= part
        try:
            metadata = current.lstat()
        except OSError as exc:
            raise SandboxError(f"results artifact parent is unavailable: {current}: {exc}") from exc
        if stat.S_ISLNK(metadata.st_mode) or not stat.S_ISDIR(metadata.st_mode):
            raise SandboxError(f"results artifact parent must be a real directory: {current}")
        if transcript and stat.S_IMODE(metadata.st_mode) & 0o077:
            raise SandboxError(f"transcript artifact parent must be owner-only: {current}")
    return root / Path(*relative.parts)


def _transcript_artifact_metadata(metadata: Any) -> tuple[str, str, int]:
    """Validate transcript metadata without touching any host path."""

    if not isinstance(metadata, dict) or set(metadata) != {"path", "sha256", "bytes", "source"}:
        raise SandboxError("transcript artifact metadata must contain only path, sha256, bytes, and source")
    relative = metadata["path"]
    expected_digest = metadata["sha256"]
    expected_size = metadata["bytes"]
    if metadata["source"] != runner_sessions.PARENT_EVENT_STREAM_SOURCE:
        raise SandboxError("transcript artifact source is not the parent event stream")
    if not isinstance(relative, str) or not re.fullmatch(r"[0-9a-f]{64}", str(expected_digest)):
        raise SandboxError("transcript artifact metadata is malformed")
    if not isinstance(expected_size, int) or isinstance(expected_size, bool):

View on GitHub (pinned to d540b00184)

Solutions

  1. Inspect the printed component: `ls -ldH <results_dir>/<component>` and confirm it is a real directory with no arrow.
  2. Replace any symlinked component with a real directory and repopulate it.
  3. Remove any file shadowing the directory name and recreate the directory.

Example fix

# before: 'transcripts' is a symlink or a file
ls results/transcripts  # -> file or symlink

# after
rm -f results/transcripts
mkdir -p results/transcripts
cp /backup/transcripts/* results/transcripts/
Defensive patterns

Strategy: validation

Validate before calling

import stat
from pathlib import Path, PurePosixPath

def artifact_parents_are_real_dirs(root: Path, relative_value: str) -> bool:
    current = root
    for part in PurePosixPath(relative_value).parts[:-1]:
        current /= part
        m = current.lstat().st_mode
        if stat.S_ISLNK(m) or not stat.S_ISDIR(m):
            return False
    return True

Prevention

When it happens

Trigger: An artifact path component is a regular file (e.g. 'transcripts' is a file, not a dir), or a symlink standing in for a directory; a crafted results tree trying to redirect through a link.

Common situations: A file accidentally created where a directory should be; a symlinked subdirectory; tampered evidence.

Related errors


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