abhigyanpatwari/GitNexus · error · SandboxError

sanitized graph source changed while scanning: {relative}

Error message

sanitized graph source changed while scanning: {relative}

What it means

Second TOCTOU guard inside the file read: after streaming the file into a payload buffer, a final os.fstat shows st_size or (st_mtime_ns, st_ctime_ns) differs from the value taken at open, or the bytes read do not match the opened size. Indicates the file was truncated/extended/rewritten during the read.

Source

Thrown at eval/workflow_bench/sanitized_graph.py:221

                    metadata.st_size,
                ):
                    raise SandboxError(f"sanitized graph source changed while opening: {relative}")
                chunks: list[bytes] = []
                remaining = MAX_GRAPH_SCRUB_FILE_BYTES + 1
                while remaining > 0:
                    chunk = os.read(descriptor, min(64 * 1024, remaining))
                    if not chunk:
                        break
                    chunks.append(chunk)
                    remaining -= len(chunk)
                payload = b"".join(chunks)
                after = os.fstat(descriptor)
                if len(payload) != opened.st_size or (opened.st_size, opened.st_mtime_ns, opened.st_ctime_ns) != (
                    after.st_size,
                    after.st_mtime_ns,
                    after.st_ctime_ns,
                ):
                    raise SandboxError(f"sanitized graph source changed while scanning: {relative}")
            finally:
                os.close(descriptor)
            if any(marker in payload for marker in marker_bytes):
                Path(entry.path).unlink()
                removed.append(relative_text)
    return tuple(sorted(removed))


def _graph_environment() -> dict[str, str]:
    env = build_sandbox_environment()
    env.update(
        {
            "GITNEXUS_HOME": SANDBOX_INDEX_REGISTRY,
            "GITNEXUS_NO_GITIGNORE": "1",
            "GITNEXUS_WORKER_POOL_SIZE": "1",
            "GITNEXUS_PARSE_CHUNK_CONCURRENCY": "1",
        }
    )

View on GitHub (pinned to d540b00184)

Solutions

  1. Quiesce the seed (no writers) for the duration of scrub and graph build.
  2. Point volatile outputs (logs, generated artifacts) outside the worktree.
  3. Retry once the writer is stopped; rebuild the seed via make_worktree.
Defensive patterns

Strategy: validation

Validate before calling

import fcntl, os

def quiesce_seed(root):
    """Hold an exclusive lock so no writer mutates files during the read."""
    fd = os.open(root, os.O_RDONLY | os.O_DIRECTORY)
    fcntl.flock(fd, fcntl.LOCK_EX)
    return fd

Prevention

When it happens

Trigger: A file in the seed is appended to, truncated, or replaced while the scrubber reads it in 64 KiB chunks (up to MAX_GRAPH_SCRUB_FILE_BYTES + 1).

Common situations: Log files or generated outputs being written live in the worktree; concurrent git operations; a sync/indexing daemon touching files mid-scan.

Related errors


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