abhigyanpatwari/GitNexus · error · SandboxError
evidence source must be a regular non-symlink file: {path}
Error message
evidence source must be a regular non-symlink file: {path} What it means
_bounded_regular_text reads an evidence file (patch, redacted excerpt) into the proposer prompt. It lstat's the path and refuses symlinks (path.is_symlink()) and any non-regular file (not stat.S_ISREG). The guard prevents a crafted results tree from redirecting the read through a symlink to an arbitrary host file before it crosses the sandbox trust boundary.
Source
Thrown at eval/workflow_bench/evolve.py:281
gitnexus/test/unit/skills-steering.test.ts before rewording any command.
- Never weaken the skills' hard gates: impact-before-edit,
detect_changes-before-commit, foreground verification.
- Keep the edit small — a rule added, sharpened, or deleted; a budget
adjusted; a phase reordered. A sprawling rewrite loses in human review even
if it wins the gate.
Finally write {proposal_path}: the failure pattern (cite task/arm/session
ids), the single change you made, the metric you expect to move and why, and
the risks. That file is the reviewer-facing case for the candidate."""
# ─── Proposer session ────────────────────────────────────────────────────────
def _bounded_regular_text(path: Path, limit: int = MAX_EVIDENCE_FILE_BYTES) -> str:
mode = path.lstat().st_mode
if path.is_symlink() or not stat.S_ISREG(mode):
raise SandboxError(f"evidence source must be a regular non-symlink file: {path}")
with path.open("rb") as handle:
if path.stat().st_size > limit:
handle.seek(-limit, os.SEEK_END)
return handle.read(limit).decode(errors="replace")
def _real_results_root(results_dir: Path) -> Path:
root = results_dir.expanduser().absolute()
try:
metadata = root.lstat()
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
View on GitHub (pinned to d540b00184)
Solutions
- Locate the offending entry: `find <evidence_dir> -type l`.
- Materialize it: `cp -L --remove-destination <link> <link>`.
- Regenerate the evidence bundle so it contains only regular files.
Example fix
# before
ln -s /shared/excerpts/run-42.patch evidence/run-42.patch
_bounded_regular_text(Path("evidence/run-42.patch"))
# after
cp /shared/excerpts/run-42.patch evidence/run-42.patch
_bounded_regular_text(Path("evidence/run-42.patch")) Defensive patterns
Strategy: validation
Validate before calling
import stat
from pathlib import Path
def evidence_file_is_regular(path: Path) -> bool:
mode = path.lstat().st_mode
return not path.is_symlink() and stat.S_ISREG(mode)
# before reading evidence
for f in evidence_dir.rglob("*"):
if f.is_file() and not evidence_file_is_regular(f):
raise ValueError(f"non-regular evidence file: {f}") Type guard
import stat
from pathlib import Path
def is_safe_evidence_file(path: Path) -> bool:
return path.exists() and not path.is_symlink() and stat.S_ISREG(path.lstat().st_mode) Prevention
- Generate evidence with copies, never symlinks.
- Audit `find <evidence_dir> -type l` in CI; it must be empty.
- Materialize any shared excerpt with cp -L --remove-destination before staging.
When it happens
Trigger: An evidence file under the staged evidence dir is a symlink (e.g. `ln -s /etc/passwd patch.diff`), a broken symlink, or a special file; a packaging step symlinked shared excerpts.
Common situations: Shared redacted excerpts reused across runs via symlinks; a proposer or upstream tool that symlinks outputs into the evidence dir; broken symlinks after the evidence root was moved.
Related errors
- results directory must not traverse symlinks: {root}
- results artifact parent must be a real directory: {current}
- transcript artifact must be a regular non-symlink file: {pat
- evidence must be a regular non-symlink file: {value}
- {label} must be a real directory: {root}
AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12).
Data as JSON: /api/errors/3c5c3c7595e69cfb.
Report an issue: GitHub.