abhigyanpatwari/GitNexus · critical · SandboxError

task asset reflink produced an invalid file: {entry.path}

Error message

task asset reflink produced an invalid file: {entry.path}

What it means

After _try_reflink reports success, _materialize_file checks that the destination's size equals entry.size. A mismatch implies the reflink produced partial or wrong content — a filesystem bug, concurrent truncation of the source between the pre-open lstat and the ioctl, or a stale manifest size.

Source

Thrown at eval/workflow_bench/task_assets.py:824

) -> int:
    metadata = source.lstat()
    if stat.S_ISLNK(metadata.st_mode) or not stat.S_ISREG(metadata.st_mode) or metadata.st_size != entry.size:
        raise SandboxError(f"task asset snapshot file changed: {entry.path}")
    temporary = destination.with_name(f".{destination.name}.{uuid.uuid4().hex}.tmp")
    source_descriptor = os.open(source, os.O_RDONLY | getattr(os, "O_CLOEXEC", 0) | getattr(os, "O_NOFOLLOW", 0))
    destination_descriptor = os.open(
        temporary,
        os.O_WRONLY | os.O_CREAT | os.O_EXCL | getattr(os, "O_CLOEXEC", 0),
        0o600,
    )
    fallback_bytes = 0
    try:
        opened = os.fstat(source_descriptor)
        if _mutation_identity(opened) != _mutation_identity(metadata):
            raise SandboxError(f"task asset snapshot file changed: {entry.path}")
        if _try_reflink(source_descriptor, destination_descriptor):
            if os.fstat(destination_descriptor).st_size != entry.size:
                raise SandboxError(f"task asset reflink produced an invalid file: {entry.path}")
        else:
            if entry.size > fallback_budget:
                raise SandboxError(
                    "task asset filesystem cannot reflink the snapshot and the buffered fallback limit would be exceeded"
                )
            os.ftruncate(destination_descriptor, 0)
            os.lseek(source_descriptor, 0, os.SEEK_SET)
            while True:
                chunk = os.read(source_descriptor, COPY_CHUNK_BYTES)
                if not chunk:
                    break
                _write_all(destination_descriptor, chunk)
                fallback_bytes += len(chunk)
            if fallback_bytes != entry.size:
                raise SandboxError(f"task asset snapshot file changed while materializing: {entry.path}")
        if _mutation_identity(opened) != _mutation_identity(os.fstat(source_descriptor)):
            raise SandboxError(f"task asset snapshot file changed while materializing: {entry.path}")
        os.fchmod(destination_descriptor, 0o600)

View on GitHub (pinned to d540b00184)

Solutions

  1. Re-prepare the snapshot so the manifest matches the on-disk file
  2. Check filesystem health (`btrfs scrub start`, `xfs_repair -n`)
  3. Eliminate concurrent truncaters; if unavoidable, place the cache on a non-clone filesystem so the buffered fallback runs with its own size check
Defensive patterns

Strategy: try-catch

Try / catch

from eval.workflow_bench.proposer_sandbox import SandboxError

try:
    snapshot.materialize(clone)
except SandboxError as exc:
    if "reflink produced an invalid file" in str(exc):
        raise SystemExit(f"reflink integrity failure; re-prepare and check fs health: {exc}") from exc
    raise

Prevention

When it happens

Trigger: The source was truncated between the lstat (which sized the manifest) and the FICLONE ioctl; the filesystem reported success but the clone is partial; manifest size was recorded against a different inode.

Common situations: Concurrent truncation of the snapshot; btrfs/xfs returning a partial clone under space pressure; manifest captured against a since-replaced file.

Related errors


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