abhigyanpatwari/GitNexus · error · SandboxError
unsafe results artifact path: {relative_value!r}
Error message
unsafe results artifact path: {relative_value!r} What it means
_results_artifact_path enforces a strict shape on each artifact's relative path before any filesystem touch: not absolute, exactly expected_parts (1 for a plain artifact, 2 for a transcript), no empty/./.. components, and transcripts must start with 'transcripts/'. This is the primary path-traversal and layout guard for results.jsonl entries.
Source
Thrown at eval/workflow_bench/evolve.py:310
except OSError as exc:
raise SandboxError(f"results directory is unavailable: {root}: {exc}") from exc
if stat.S_ISLNK(metadata.st_mode) or not stat.S_ISDIR(metadata.st_mode):
raise SandboxError(f"results directory must be a real non-symlink directory: {root}")
if root.resolve(strict=True) != root:
raise SandboxError(f"results directory must not traverse symlinks: {root}")
return root
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"}:View on GitHub (pinned to d540b00184)
Solutions
- Inspect the offending row: the message prints relative_value!r; find that exact string in results.jsonl.
- Rewrite the row's path to a single component (plain artifact) or 'transcripts/<name>' (transcript).
- Ensure the runner that writes results.jsonl normalizes paths to the contract.
Example fix
# before (results.jsonl row)
{"path": "../hidden/secret", "sha256": "...", ...}
# after
{"path": "secret", "sha256": "...", ...}
# and for transcripts
{"path": "transcripts/run-42.json", "source": "parent-captured-stream-json", ...} Defensive patterns
Strategy: validation
Validate before calling
from pathlib import PurePosixPath
def artifact_path_ok(relative_value: str, *, transcript: bool) -> bool:
p = PurePosixPath(relative_value)
expected = 2 if transcript else 1
return (
not p.is_absolute()
and len(p.parts) == expected
and not any(part in {"", ".", ".."} for part in p.parts)
and (not transcript or p.parts[0] == "transcripts")
) Prevention
- Normalize every results.jsonl path to the contract before writing.
- Never store absolute paths or nested layouts in artifact rows.
- Add a unit test that fuzzes traversal strings against the writer.
When it happens
Trigger: A results row carries a path like '../secret', '/etc/passwd', 'a/b/c' (too many parts), '' or '.', or a transcript path that does not start with 'transcripts/'.
Common situations: A custom runner emits absolute paths or nested layouts; a hand-edited results.jsonl; a transcript path recorded without the transcripts/ prefix; path-escape attempts in crafted evidence.
Related errors
- {label} contains an unsafe path component: {relative}
- {label} must be a real non-symlink directory: {path}
- {label} must be a regular non-symlink file: {path}
- {label} changed while opening: {path}
- evidence source must be a regular non-symlink file: {path}
AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12).
Data as JSON: /api/errors/1b9267d9a1694bef.
Report an issue: GitHub.