databendlabs/databend · error

write returned zero bytes

Error message

write returned zero bytes

What it means

`write_direct` performs aligned `pwrite` calls on a DMA (O_DIRECT) file in a loop until all data is written. POSIX allows short writes, but a return of 0 bytes means no progress was made — treated as `ErrorKind::WriteZero`. Continuing would loop forever, so the code aborts with this io::Error.

Solutions

  1. Check disk space and quotas on the device holding the data file (`df -h`).
  2. Ensure the file was opened correctly for O_DIRECT and offsets/buffer are properly aligned (the code aligns offsets, but verify the mount supports O_DIRECT).
  3. Verify the fd is still valid and the file wasn't truncated concurrently by another process.
  4. Retry the operation; if it reproduces on a specific filesystem, test on a filesystem known to support O_DIRECT (ext4/xfs) or disable direct I/O if configurable.
Defensive patterns

Strategy: try-catch

Validate before calling

// before writing, check space and fd state
fn can_write(fd: &File, dir: &Path) -> std::io::Result<()> {
    let meta = fs::metadata(dir)?;
    let avail = fs2::available_space(dir)?;
    if avail < meta.len() { return Err(std::io::Error::new(std::io::ErrorKind::StorageFull, "low disk")); }
    Ok(())
}

Try / catch

match writer.write_direct(&buf) {
    Err(e) if e.kind() == std::io::ErrorKind::WriteZero => handle_disk_full_or_io_failure(e),
    other => other,
}

Prevention

When it happens

Trigger: `rustix::io::pwrite` on the direct-I/O file returns `Ok(0)`, e.g. at/behind EOF semantics on certain filesystems, a full disk returning zero, or the fd reaching an unexpected end condition mid-write.

Common situations: Disk full or quota exhausted on the backing device, filesystem not honoring O_DIRECT writes at the requested aligned offset, unusual fuse/network filesystems that return 0 instead of an error.

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/4c841c09eb7d5826. Report an issue: GitHub.

Appendix: source

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

    pub fn align_down(&self, value: usize) -> usize {
        self.alignment.align_down(value)
    }

    /// Return the alignment requirement for this file. The returned alignment value can be used
    /// to allocate a buffer to use with this file:
    pub fn alignment(&self) -> Alignment {
        self.alignment
    }

    fn write_direct(&mut self, buf: &mut DmaBuffer) -> io::Result<usize> {
        let data = buf.as_slice();
        let mut written = 0;
        let offset = self.align_down(self.offset);

        while written < data.len() {
            match rustix::io::pwrite(&self.fd, &data[written..], (offset + written) as _) {
                Ok(0) => {
                    return Err(io::Error::new(
                        io::ErrorKind::WriteZero,
                        "write returned zero bytes",
                    ));
                }
                Ok(n) => {
                    written += n;
                    self.offset = offset + written
                }
                Err(err) => {
                    if err.kind() == io::ErrorKind::Interrupted {
                        continue;
                    }
                    return Err(err.into());
                }
            }
        }
        buf.clear();
        Ok(written)

View on GitHub (pinned to 288d84d76e)