EpicGames/lore · error · io::Error (WriteZero)
file refused further writes
Error message
file refused further writes
What it means
The io_uring write_all_at loop treats a completed write of 0 bytes as "the file refused further writes" and fails with ErrorKind::WriteZero. This preserves all-or-nothing semantics: either the whole buffer is written or the caller gets an error, never a silent partial write.
Solutions
- Free disk space / raise quota, then retry the write from a known offset.
- Reopen the file and restart the write; a 0-byte write mid-loop means prior bytes may be durable, so rewrite the whole buffer if idempotence matters.
- Stat available space before large writes and stream in chunks with ENOSPC handling.
- Ensure the file is opened with write/append permissions and the offset is within a supportable range.
Example fix
// before
file.write_all_at(&data, offset).await?; // WriteZero on full disk
// after
if free_bytes(&path)? < data.len() as u64 {
return Err(std::io::Error::new(std::io::ErrorKind::StorageFull, "volume full"));
}
file.write_all_at(&data, offset).await?; Defensive patterns
Strategy: try-catch
Validate before calling
// Rust
if fs2::available_space(&path)? < data.len() as u64 {
return Err(std::io::Error::new(std::io::ErrorKind::StorageFull, "volume full before async write"));
} Try / catch
// Rust
match file.write_all_at(&data, offset).await {
Ok(()) => {}
Err(e) if e.kind() == std::io::ErrorKind::WriteZero => { /* ENOSPC: free space, reopen, rewrite whole buffer */ }
Err(e) => return Err(e.into()),
} Prevention
- Check volume free space before large async writes
- Treat WriteZero as disk-full, not a transient glitch
- Avoid concurrent truncation; coordinate with locks or atomic replace
- Keep writes idempotent so a failed write_all_at can be safely retried in full
When it happens
Trigger: Calling write_all_at when an io_uring write completes with 0 bytes: full volume/quota, file truncated concurrently, write at an offset the filesystem cannot extend, or a special-file target that stopped accepting data.
Common situations: Disk-full during a large async write; ENOSPC surfacing as a zero-length completion; concurrent truncation by another task/process; container disk limits hit mid-write.
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
- file refused further writes
- file ended before the requested read length
- bytes exceeds the byte whole-file limit; open the file and…
- file ended before the requested read length
- file ended before the requested read length
AI-assisted analysis of EpicGames/lore@074eb0b0d1 (2026-09-13).
Data as JSON: /api/errors/cb663d0f9f0a9580.
Report an issue: GitHub.
Appendix: source
Thrown at lore-io/src/uring.rs:405
len: usize,
offset: u64,
) -> std::io::Result<B> {
let mut buffer = buffer;
let mut done = 0;
while done < len {
let entry = write_entry(
&file,
buffer.as_ref(),
done,
len - done,
offset + done as u64,
);
let (payload, result) = self.submit(entry, payload(buffer, &file))?.await;
buffer = payload.buffer;
match interpret(result)? {
Progress::Interrupted => {}
Progress::Bytes(0) => {
return Err(std::io::Error::new(
std::io::ErrorKind::WriteZero,
"file refused further writes",
));
}
Progress::Bytes(written) => done += written,
}
}
Ok(buffer)
}
/// Scatters one read across the segments, returning how many bytes it filled.
///
/// The iovec array is rebuilt for every pass because it travels into the op entry with the
/// segments, and a pass that made partial progress needs a different array anyway: the skip
/// moves and the leading segments drop out.
pub(crate) async fn read_vectored_at<B: StableBufListMut>(
&self,
file: Arc<File>,View on GitHub (pinned to 074eb0b0d1)