abhigyanpatwari/GitNexus · error · OSError

short write while staging overlay replacement

Error message

short write while staging overlay replacement

What it means

All errors below are raised by internal helpers of `eval/workflow_bench/promotion_apply.py` and propagate to the caller of the public entry points: `apply_promoted_overlay(overlay, repo_root, *, expected_digest, expected_target_bases)`, `destination_base_digests(overlay, repo_root)`, `committed_destination_base_digests(overlay, repo_root, *, ref)` and `freeze_overlay(overlay, destination)`. The module applies promoted skill overlays across the canonical skill tree plus its shipped mirrors (`gitnexus/skills`, `gitnexus-claude-plugin/skills`) in a TOCTOU-hardened, symlink-rejecting, descriptor-bound transaction. In `_stage_replacement_at`, `os.write(descriptor, view)` returned a value <= 0 while a non-empty buffer remained. POSIX `write(2)` does not return 0 for a non-empty regular-file buffer; this signals the write could not complete — disk full, quota exceeded, or an I/O error surfaced as a short write.

Source

Thrown at eval/workflow_bench/promotion_apply.py:347

def _stage_replacement_at(parent_descriptor: int, content: bytes, mode: int) -> str:
    for _ in range(100):
        name = f".wfevolve-{secrets.token_hex(16)}"
        try:
            descriptor = os.open(
                name,
                os.O_WRONLY | os.O_CREAT | os.O_EXCL | getattr(os, "O_CLOEXEC", 0),
                stat.S_IMODE(mode),
                dir_fd=parent_descriptor,
            )
        except FileExistsError:
            continue
        try:
            os.fchmod(descriptor, stat.S_IMODE(mode))
            view = memoryview(content)
            while view:
                written = os.write(descriptor, view)
                if written <= 0:
                    raise OSError("short write while staging overlay replacement")
                view = view[written:]
            os.fsync(descriptor)
        except BaseException as exc:
            os.close(descriptor)
            try:
                os.unlink(name, dir_fd=parent_descriptor)
                os.fsync(parent_descriptor)
            except OSError as cleanup_exc:
                raise _StagingCleanupError(name, exc, cleanup_exc) from exc
            raise
        else:
            os.close(descriptor)
            try:
                os.fsync(parent_descriptor)
            except OSError as exc:
                try:
                    os.unlink(name, dir_fd=parent_descriptor)
                    os.fsync(parent_descriptor)

View on GitHub (pinned to d540b00184)

Solutions

  1. Check free space on the filesystem holding the checkout: `shutil.disk_usage(root)`.
  2. Free space or raise quota; move the checkout to a larger volume.
  3. Catch `OSError` and surface it — the module already unlinks the partial staging file and re-raises.

Example fix

// before
# staged write fails with ENOSPC
apply_promoted_overlay(overlay, repo_root=root)
// after
free = shutil.disk_usage(root).free
need = sum(len(c) for _, c in candidate_overlay_payload(overlay)[1]) * 3  # candidate + backup + headroom
if free < need:
    raise SystemExit(f'insufficient free space: {free} < {need}')
apply_promoted_overlay(overlay, repo_root=root)
Defensive patterns

Strategy: try-catch

Validate before calling

import shutil
free = shutil.disk_usage(root).free
need = sum(len(c) for _, c in candidate_overlay_payload(overlay)[1]) * 3
if free < need:
    raise SystemExit(f'need ~{need} bytes free, have {free}')

Try / catch

except OSError as exc:
    if 'short write while staging' in str(exc):
        raise SystemExit(f'staging write incomplete (disk full / I/O error): {exc}') from exc

Prevention

When it happens

Trigger: Staging a replacement content buffer whose `os.write` returns 0 or negative: out of space on the filesystem holding the parent dir, quota hit, or a failing disk.

Common situations: CI runner out of disk; small tmpfs for the checkout; user quota exceeded; failing/read-only filesystem; overly large overlay payload near the 4 MiB `MAX_CANDIDATE_OVERLAY_BYTES` cap.

Related errors


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