rustfs/rustfs · error · std::io::Error

Unexpected EOF

Error message

Unexpected EOF

What it means

Thrown by read_more in rustfs-filemeta's version module while incrementally reading xl.meta. The buffer must grow to read_size more bytes, but the guard fails because has_full is true (the caller already read the entire file into the buffer — there is nothing more to fetch) or read_size > total_size (the header claims more bytes than the file's stat size). Both mean the metadata layout contradicts the actual file length, so it fails with UnexpectedEof instead of reading garbage.

Source

Thrown at crates/filemeta/src/filemeta/version.rs:3500

    Ok(fi)
}

async fn read_more<R: AsyncRead + Unpin>(
    reader: &mut R,
    buf: &mut Vec<u8>,
    total_size: usize,
    read_size: usize,
    has_full: bool,
) -> Result<()> {
    use tokio::io::AsyncReadExt;
    let has = buf.len();

    if has >= read_size {
        return Ok(());
    }

    if has_full || read_size > total_size {
        return Err(Error::other(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "Unexpected EOF")));
    }

    let extra = read_size - has;
    if buf.capacity() >= read_size {
        // Extend the buffer if we have enough space.
        buf.resize(read_size, 0);
    } else {
        buf.extend(vec![0u8; extra]);
    }

    reader.read_exact(&mut buf[has..]).await?;
    Ok(())
}

pub async fn read_xl_meta_no_data<R: AsyncRead + Unpin>(reader: &mut R, size: usize) -> Result<Vec<u8>> {
    use tokio::io::AsyncReadExt;

    let mut initial = size;

View on GitHub (pinned to 9e6e02ea09)

Solutions

  1. Heal the bucket/object to rebuild xl.meta from the remaining intact shards
  2. Verify the xl.meta file size on each shard (ls -l on the xl-meta path across drives) to find which copies are truncated
  3. Restore the object from backup if all copies are short
  4. Replace/monitor the failing drive if truncation recurs on one node
Defensive patterns

Strategy: try-catch

Validate before calling

// Before reading, sanity-check the stat size against the header's declared extent
if metadata_bytes_header_len > stat_size {
    // layout contradicts file length: truncated/corrupt xl.meta -> heal instead of read
}

Try / catch

match read_xl_meta_no_data(&mut reader, size).await {
    Err(e) if matches!(e, rustfs_filemeta::error::Error::Unexpected) => {
        // header extent exceeds the file: truncated metadata, heal the object
    }
    other => other?,
}

Prevention

When it happens

Trigger: read_xl_meta_no_data on an xl.meta whose msgpack bytes-header size (or +CRC trailer extent) exceeds the file's stat size, or exceeds META_DATA_READ_DEFAULT with the whole file already buffered: a truncated or corrupt xl.meta, or a header from a different (longer) version of the file.

Common situations: Truncated xl.meta from power loss; disk corruption shrinking the file; size/header disagreement after partial overwrite; running the dump_fileinfo example tool over a corrupt xl.meta; heal reading a damaged metadata shard.

Related errors


AI-assisted analysis of rustfs/rustfs@9e6e02ea09 (2026-08-16). Data as JSON: /api/errors/485cbaa4d99d7505. Report an issue: GitHub.