abhigyanpatwari/GitNexus · critical · SandboxError
transcript artifact must be a regular non-symlink file: {pat
Error message
transcript artifact must be a regular non-symlink file: {path} What it means
Raised by `_bound_transcript_artifact` when the transcript file is a symlink or not a regular file (directory, fifo, socket, device). Symlinks are blocked because they can escape the results root.
Source
Thrown at eval/workflow_bench/evolve.py:394
relative, _, _ = _transcript_artifact_metadata(artifact)
normalized = _normalized_transcript_artifact_path(relative)
if normalized in seen_paths:
raise SandboxError(f"duplicate transcript artifact path: {normalized}")
seen_paths.add(normalized)
artifacts_by_row.append(artifacts)
return artifacts_by_row
def _bound_transcript_artifact(root: Path, metadata: Any) -> str:
relative, expected_digest, expected_size = _transcript_artifact_metadata(metadata)
path = _results_artifact_path(root, relative, transcript=True)
try:
before = path.lstat()
except OSError as exc:
raise SandboxError(f"transcript artifact is unavailable: {path}: {exc}") from exc
if stat.S_ISLNK(before.st_mode) or not stat.S_ISREG(before.st_mode):
raise SandboxError(f"transcript artifact must be a regular non-symlink file: {path}")
if stat.S_IMODE(before.st_mode) & 0o077:
raise SandboxError(f"transcript artifact must be owner-only: {path}")
if before.st_size != expected_size:
raise SandboxError(f"transcript artifact size does not match its results row: {path}")
descriptor = os.open(path, os.O_RDONLY | getattr(os, "O_NOFOLLOW", 0))
try:
opened = os.fstat(descriptor)
if not stat.S_ISREG(opened.st_mode) or opened.st_dev != before.st_dev or opened.st_ino != before.st_ino:
raise SandboxError(f"transcript artifact changed while opening: {path}")
digest = hashlib.sha256()
content = bytearray()
while chunk := os.read(descriptor, 64 * 1024):
digest.update(chunk)
content.extend(chunk)
if len(content) > MAX_EVIDENCE_FILE_BYTES:
del content[: len(content) - MAX_EVIDENCE_FILE_BYTES]
after = os.fstat(descriptor)View on GitHub (pinned to d540b00184)
Solutions
- Replace the symlink/irregular node with a real, owned regular file containing the transcript bytes.
- Re-run the benchmark so the runner writes genuine transcript files.
- If this appears unexpectedly, treat it as a potential integrity violation and regenerate the results from a trusted source.
Defensive patterns
Strategy: validation
Validate before calling
import stat
for r in evidence:
for a in r.get('transcript_artifacts', []):
p = results_dir / a['path']
m = p.lstat().st_mode
if stat.S_ISLNK(m) or not stat.S_ISREG(m):
raise ValueError(f'transcript is not a regular file: {p}') Prevention
- Never symlink files into a results dir; copy real bytes.
- After restoring a results dir from archive, lstat every transcript to confirm S_ISREG.
When it happens
Trigger: A file at `transcripts/<name>` that is a symbolic link, or whose lstat mode is not S_ISREG (e.g. a directory was created where a transcript file is expected).
Common situations: Someone symlinked a transcript to shared storage, a broken restore left a directory in place of a file, or an adversarial/tampered results dir tries to redirect reads.
Related errors
- evidence source must be a regular non-symlink file: {path}
- transcript artifact must be owner-only: {path}
- {label} must be a real non-symlink directory: {path}
- {label} must be a regular non-symlink file: {path}
- results directory must not traverse symlinks: {root}
AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12).
Data as JSON: /api/errors/33ac5f5ccaeac5f1.
Report an issue: GitHub.