astrid-runtime/astrid · error · io::Error
vectored frame append made no progress
Error message
vectored frame append made no progress
What it means
This WriteZero error is raised by write_all_vectored when a vectored (scatter/gather) write of prepared durable frames reports 0 bytes written. A vectored write that returns Ok(0) means no progress was made, which would otherwise cause an infinite retry loop in the caller. The library treats zero-progress appends as an I/O fault and aborts the frame-batch append.
Solutions
- Check disk space, quota, and inode availability on the storage device holding the durable log
- Retry the append operation; transient zero-progress writes often clear once the device recovers
- If wrapping the writer, fix write_vectored to return Err(WouldBlock) or a nonzero count instead of Ok(0)
- Verify the target file handle is still open and valid; reopen the journal if the handle went stale
Example fix
// before
writer.write_vectored(&bufs)?; // returns Ok(0) silently
// after
let n = writer.write_vectored(&bufs)?;
if n == 0 {
return Err(io::Error::new(io::ErrorKind::WriteZero, "no progress"));
} Defensive patterns
Strategy: try-catch
Validate before calling
// before appending
let avail = fs4::available_space(log_path)?;
if avail < required_batch_bytes { return Err("insufficient disk space".into()); } Try / catch
match result {
Err(e) if e.kind() == io::ErrorKind::WriteZero => {
// check disk space / handle validity, then retry append
eprintln!("vectored append made no progress: {e}");
}
...
} Prevention
- Monitor free disk space and quotas on the journal volume
- Do not wrap the journal writer with custom write_vectored implementations that can return Ok(0)
- Keep retry/backoff logic around appends in the caller
- Alert on WriteZero occurrences as device-health signals
When it happens
Trigger: Calling append_prepared_frames (which routes through write_all_vectored) when the underlying writer accepts zero bytes, e.g. writing to a closed/full pipe, a file at a hard resource limit, or a writer whose write_vectored implementation spuriously returns Ok(0). vectored_append_retries_interrupted_and_short_writes surfaces it after retries are exhausted.
Common situations: Disk full or quota exceeded on the journal device; running inside a container with a frozen filesystem; a custom/wrapped writer with a buggy write_vectored that returns 0 instead of WouldBlock; EINTR-style interruptions the retry loop could not recover from.
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
- audit read failed
- destination.as_str()
- invalid Astrid volume record length at
- invalid interior Astrid volume record length at
- non-UTF-8 region name
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/dffbfd29942a8e23.
Report an issue: GitHub.
Appendix: source
Thrown at crates/astrid-storage/src/engine/durable/format/prepared.rs:115
fn write_all_vectored<W: Write + ?Sized>(
writer: &mut W,
slices: &mut [IoSlice<'_>],
) -> Result<(), DurableError> {
let mut remaining = slices;
while !remaining.is_empty() {
let written = loop {
match writer.write_vectored(remaining) {
Ok(written) => break written,
Err(source) if source.kind() == io::ErrorKind::Interrupted => {},
Err(source) => {
return Err(io_error("append prepared durable frame batch", source));
},
}
};
if written == 0 {
return Err(io_error(
"append prepared durable frame batch",
io::Error::new(
io::ErrorKind::WriteZero,
"vectored frame append made no progress",
),
));
}
IoSlice::advance_slices(&mut remaining, written);
}
Ok(())
}
#[cfg(test)]
mod tests {
use std::fs::File;
use std::io::{Read, Seek, SeekFrom};
use super::*;
use crate::engine::durable::append_frames;
View on GitHub (pinned to affd8760f4)