abhigyanpatwari/GitNexus · critical · SandboxError
transcript artifact changed while reading: {path}
Error message
transcript artifact changed while reading: {path} What it means
Raised by `_bound_transcript_artifact` when, after streaming the file to compute its digest, a final `os.fstat` reports different size or mtime_ns than the fstat taken just before reading. The file changed while being read.
Source
Thrown at eval/workflow_bench/evolve.py:414
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)
if (opened.st_size, opened.st_mtime_ns) != (after.st_size, after.st_mtime_ns):
raise SandboxError(f"transcript artifact changed while reading: {path}")
finally:
os.close(descriptor)
if digest.hexdigest() != expected_digest:
raise SandboxError(f"transcript artifact digest does not match its results row: {path}")
return bytes(content).decode(errors="replace")
def proposer_evidence_entries(
*,
results_dir: Path | None,
evidence: list[dict[str, Any]],
learnings: list[dict[str, Any]],
gate_summary: list[str],
) -> dict[str, Any]:
"""Only structured, bounded evidence crosses into the proposer."""
artifacts_by_row = _preflight_transcript_artifacts(evidence)
entries: dict[str, Any] = {View on GitHub (pinned to d540b00184)
Solutions
- Wait for the producing runner to fully finish writing transcripts before feeding its results dir to evolve.
- Copy the results dir to a stable, read-only location and point --seed-results there.
- Retry once the concurrent writer is gone; if it recurs, investigate filesystem-level tampering.
Defensive patterns
Strategy: try-catch
Try / catch
from workflow_bench.proposer_sandbox import SandboxError
try:
proposer_evidence_entries(results_dir=rd, evidence=ev, learnings=lr, gate_summary=gs)
except SandboxError as exc:
if 'changed while reading' in str(exc):
raise RuntimeError('transcript mutated mid-read; wait for writer to finish') from exc
raise Prevention
- Snapshot results into a read-only copy before running evolve.
- Do not run evolve against a results dir a runner is still populating.
When it happens
Trigger: The transcript file is being written/appended/truncated concurrently with the read loop, so its size or mtime_ns drifts during `os.read`.
Common situations: A live runner still streaming into the same transcripts/ file, log rotation, or a results dir on a filesystem another job is mutating.
Related errors
- transcript artifact changed while opening: {path}
- {label} changed while opening: {path}
- transcript artifact must be a regular non-symlink file: {pat
- transcript artifact must be owner-only: {path}
- Compound Engineering plugin file changed during validation:
AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12).
Data as JSON: /api/errors/1e8a8b61d8306b26.
Report an issue: GitHub.