databendlabs/databend · error

writev returned zero bytes

Error message

writev returned zero bytes

What it means

`writev_all` writes multiple I/O slices with `pwritev` at a given offset, looping until all slices are consumed. A `pwritev` returning 0 means no bytes were written and no progress is possible, so it aborts with `ErrorKind::WriteZero` to avoid an infinite loop. Reached via `flush_if_full`/`flush_inner` when flushing buffered DMA writes.

Solutions

  1. Check free disk space and quotas on the target device (`df -h`, project quotas).
  2. Confirm the mount supports O_DIRECT + pwritev correctly (ext4/xfs are safe; some network/FUSE filesystems are not).
  3. Ensure no concurrent truncation of the data file by other processes.
  4. Retry the write; if deterministic, fall back to non-direct I/O or re-align buffers.
Defensive patterns

Strategy: try-catch

Validate before calling

// check disk headroom before flushing
fn has_headroom(dir: &Path, need: u64) -> std::io::Result<bool> {
    Ok(fs2::available_space(dir)? > need)
}

Try / catch

match writer.flush() {
    Err(e) if e.kind() == std::io::ErrorKind::WriteZero => handle_flush_failure(e), // disk full / fs issue
    other => other,
}

Prevention

When it happens

Trigger: During buffer flush, `rustix::io::pwritev` returns `Ok(0)` — no bytes accepted by the kernel for the aligned offset, e.g. disk full at EOF, filesystem oddity, or an invalid fd state.

Common situations: Disk/quota exhaustion, writes to a file on a filesystem with broken vectored-I/O support, file truncated concurrently by another process, O_DIRECT incompatibility with the buffer alignment.

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 databendlabs/databend@288d84d76e (2026-09-11). Data as JSON: /api/errors/b961fa731819968d. Report an issue: GitHub.

Appendix: source

Thrown at src/common/base/src/base/dma.rs:717

    }

    pub fn flush_and_close(&mut self, mut file: SyncDmaFile) -> io::Result<usize> {
        self.flush_inner(&mut file, true)?;
        Ok(file.length())
    }

    pub fn flush(&mut self, file: &mut SyncDmaFile) -> io::Result<()> {
        self.flush_inner(file, false)
    }
}

fn writev_all(fd: impl AsFd, mut slices: &mut [IoSlice<'_>], offset: usize) -> io::Result<usize> {
    let mut written = 0;

    while !slices.is_empty() {
        match rustix::io::pwritev(fd.as_fd(), slices, (offset + written) as _) {
            Ok(0) => {
                return Err(io::Error::new(
                    io::ErrorKind::WriteZero,
                    "writev returned zero bytes",
                ));
            }
            Ok(n) => {
                written += n;
                IoSlice::advance_slices(&mut slices, n);
            }
            Err(err) => {
                if err.kind() == io::ErrorKind::Interrupted {
                    continue;
                }
                return Err(err.into());
            }
        };
    }

    Ok(written)

View on GitHub (pinned to 288d84d76e)