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

  1. Wait for the producing runner to fully finish writing transcripts before feeding its results dir to evolve.
  2. Copy the results dir to a stable, read-only location and point --seed-results there.
  3. 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

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


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