EpicGames/lore · error · io::Error

file refused further writes

Error message

file refused further writes

What it means

write_all_at could not complete the requested write: the underlying write backend (IOCP) reported zero bytes written at some point mid-loop, so the file accepted no further data. The library surfaces this as ErrorKind::WriteZero rather than silently returning a short write, guaranteeing the caller that the full buffer was either written or the call fails.

Solutions

  1. Check disk space / quota on the target volume and free space or move the file.
  2. Verify the file was not truncated or replaced by another process; reopen and retry the whole write.
  3. Confirm the file is opened for writing at the requested offset and the volume supports files that large.
  4. Retry with a smaller write or use plain filesystem paths rather than special devices.

Example fix

// before
file.write_all_at(&buf, offset).await?; // panics/unhandled WriteZero on full disk
// after
match file.write_all_at(&buf, offset).await {
    Ok(()) => {}
    Err(e) if e.kind() == std::io::ErrorKind::WriteZero => {
        ensure_disk_space(&path)?;
        file.write_all_at(&buf, offset).await?;
    }
    Err(e) => return Err(e),
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Rust
let free = fs2::available_space(&path)?;
if free < buf.len() as u64 { return Err(std::io::Error::new(std::io::ErrorKind::StorageFull, "no space")); }

Try / catch

// Rust
match file.write_all_at(&buf, offset).await {
    Ok(()) => {}
    Err(e) if e.kind() == std::io::ErrorKind::WriteZero => { /* free space / reopen / retry once */ }
    Err(e) => return Err(e.into()),
}

Prevention

When it happens

Trigger: Calling write_all_at on a file whose writer end is effectively closed or whose size cannot grow — e.g. writing past a size-capped file, a full/quota-exhausted volume, or a file opened in a mode that truncates or rejects further writes — and the overlapped write operation completes successfully with 0 bytes.

Common situations: Disk full or quota exceeded mid-write; the file was truncated/deleted by another process between writes; writing to a special file (pipe, named pipe) that closed; filesystem size limits hit at the offset being written.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


AI-assisted analysis of EpicGames/lore@074eb0b0d1 (2026-09-13). Data as JSON: /api/errors/0bdb72663d3ac142. Report an issue: GitHub.

Appendix: source

Thrown at lore-io/src/iocp.rs:447

    /// Writes all `len` bytes. See [`Self::read_exact_at`] for why each pass is its own
    /// submission.
    pub(crate) async fn write_all_at<B: StableBuf>(
        &self,
        file: Arc<File>,
        buffer: B,
        len: usize,
        offset: u64,
    ) -> std::io::Result<B> {
        let mut buffer = buffer;
        let mut done = 0;
        while done < len {
            let (payload, result) = self
                .write(&file, buffer, done, len - done, offset + done as u64)
                .await;
            buffer = payload.buffer;
            match result? {
                0 => {
                    return Err(std::io::Error::new(
                        std::io::ErrorKind::WriteZero,
                        "file refused further writes",
                    ));
                }
                written => done += written,
            }
        }
        Ok(buffer)
    }

    /// Scatters a read across the segments, one segment per operation.
    ///
    /// Windows has no positional scatter/gather call for an ordinary handle — `ReadFileScatter`
    /// takes page-aligned, sector-sized segments only — so a segment list becomes a sequence of
    /// operations however it is issued. Issuing them here rather than forwarding to
    /// [`PsyncDriver`] is what keeps them off the pool: the psync backend walks the segments with
    /// blocking positional calls inside one dispatch, holding a thread for the whole list.
    ///

View on GitHub (pinned to 074eb0b0d1)