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

  1. 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)).
  2. 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).
  3. 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.
  4. 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

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


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