abhigyanpatwari/GitNexus · error · SandboxError
evidence path is unreadable: {value}: {exc}
Error message
evidence path is unreadable: {value}: {exc} What it means
Raised by _evidence_bytes when a Path evidence value cannot be lstat()'d. The sandbox stages an owner-only redacted evidence bundle and must inspect each file's mode before reading it; an OSError from lstat (ENOENT, EACCES, ELOOP) means the file is not inspectable, so staging aborts before any bytes are read.
Source
Thrown at eval/workflow_bench/proposer_sandbox.py:196
)
def redact_text(text: str, secrets: Sequence[str] = ()) -> str:
for secret in secrets:
if secret:
text = text.replace(secret, "[REDACTED]")
text = _TOKEN_PATTERNS[0].sub("[REDACTED]", text)
text = _TOKEN_PATTERNS[1].sub("[REDACTED]", text)
text = _TOKEN_PATTERNS[2].sub(r"\1[REDACTED]", text)
return _TOKEN_PATTERNS[3].sub(r"\1[REDACTED]@", text)
def _evidence_bytes(value: Any, secrets: Sequence[str]) -> bytes:
if isinstance(value, Path):
try:
mode = value.lstat().st_mode
except OSError as exc:
raise SandboxError(f"evidence path is unreadable: {value}: {exc}") from exc
if value.is_symlink() or not stat.S_ISREG(mode):
raise SandboxError(f"evidence must be a regular non-symlink file: {value}")
if value.stat().st_size > MAX_EVIDENCE_FILE_BYTES:
raise SandboxError(f"evidence exceeds the per-file limit: {value}")
raw = value.read_bytes()
return redact_text(raw.decode(errors="replace"), secrets).encode()
if isinstance(value, bytes):
raw = value
elif isinstance(value, str):
raw = value.encode()
else:
raw = (json.dumps(value, sort_keys=True, separators=(",", ":")) + "\n").encode()
return redact_text(raw.decode(errors="replace"), secrets).encode()
def stage_evidence_bundle(
destination: Path,
entries: Mapping[str, Any],View on GitHub (pinned to d540b00184)
Solutions
- Verify the path exists and is readable from the process that calls stage_evidence_bundle before passing it: print(value, value.exists(), os.access(value.parent, os.R_OK)).
- Ensure the Path is absolute and resolved under the clone/workspace root the staging process actually sees (staging runs in the parent driver, not inside bwrap).
- Regenerate the evidence file if a prior phase was responsible for producing it, or switch the entry to bytes/str if the content is already in memory.
- If the file lives under a transient mount, move it to a stable location (e.g. the session private_root) before staging.
Example fix
// before
bundle = stage_evidence_bundle(dest, {"log.txt": Path("run.log")})
// after
log = (clone / "run.log").resolve()
if not log.is_file():
raise FileNotFoundError(f"missing evidence: {log}")
bundle = stage_evidence_bundle(dest, {"log.txt": log}) Defensive patterns
Strategy: validation
Validate before calling
def valid_evidence_path(value: Path) -> bool:
try:
mode = value.lstat().st_mode
return value.is_file() and not stat.S_ISLNK(mode)
except OSError:
return False
# before staging:
for name, v in entries.items():
if isinstance(v, Path) and not valid_evidence_path(v):
raise ValueError(f'{name}: evidence path unreadable') Type guard
from pathlib import Path
import stat
def is_readable_regular_file(value: Path) -> bool:
if not isinstance(value, Path):
return False
try:
mode = value.lstat().st_mode
except OSError:
return False
return stat.S_ISREG(mode) and not stat.S_ISLNK(mode) Try / catch
try:
bundle = stage_evidence_bundle(dest, entries, secrets=secrets)
except SandboxError as exc:
if 'unreadable' in str(exc):
log.warning('evidence path unreadable, regenerating: %s', exc)
regenerate_evidence()
raise
raise Prevention
- Resolve evidence paths to absolute form under a known root before staging.
- Run a preflight lstat/read check on every Path entry.
- Generate evidence files in a stable location that survives across phases.
- Avoid staging paths from transient mounts.
When it happens
Trigger: Calling stage_evidence_bundle (or _evidence_bytes) with an entry whose value is a pathlib.Path that does not exist, is on an unreadable parent directory, has a dangling symlink in the path, or sits behind a permission boundary the staging process cannot cross.
Common situations: Test/oracle code resolves a path relative to the wrong cwd; evidence path was created under a tmp dir that got cleaned up between phases; NFS/sshfs mount dropped; CI runs as a user lacking read on the parent dir; a relative Path was constructed against the host root instead of the clone root.
Related errors
- evidence source must be a regular non-symlink file: {path}
- results directory is unavailable: {root}: {exc}
- unsafe results artifact path: {relative_value!r}
- transcript artifact metadata must contain only path, sha256,
- transcript_artifacts must be a list
AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12).
Data as JSON: /api/errors/2331f796133d308d.
Report an issue: GitHub.