{"record":{"id":"485cbaa4d99d7505","repo":"rustfs/rustfs","slug":"unexpected-eof","errorCode":null,"errorMessage":"Unexpected EOF","messagePattern":"Unexpected EOF","errorType":"exception","errorClass":"std::io::Error","httpStatus":null,"severity":"error","filePath":"crates/filemeta/src/filemeta/version.rs","lineNumber":3500,"sourceCode":"    Ok(fi)\n}\n\nasync fn read_more<R: AsyncRead + Unpin>(\n    reader: &mut R,\n    buf: &mut Vec<u8>,\n    total_size: usize,\n    read_size: usize,\n    has_full: bool,\n) -> Result<()> {\n    use tokio::io::AsyncReadExt;\n    let has = buf.len();\n\n    if has >= read_size {\n        return Ok(());\n    }\n\n    if has_full || read_size > total_size {\n        return Err(Error::other(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, \"Unexpected EOF\")));\n    }\n\n    let extra = read_size - has;\n    if buf.capacity() >= read_size {\n        // Extend the buffer if we have enough space.\n        buf.resize(read_size, 0);\n    } else {\n        buf.extend(vec![0u8; extra]);\n    }\n\n    reader.read_exact(&mut buf[has..]).await?;\n    Ok(())\n}\n\npub async fn read_xl_meta_no_data<R: AsyncRead + Unpin>(reader: &mut R, size: usize) -> Result<Vec<u8>> {\n    use tokio::io::AsyncReadExt;\n\n    let mut initial = size;","sourceCodeStart":3482,"sourceCodeEnd":3518,"githubUrl":"https://github.com/rustfs/rustfs/blob/9e6e02ea09c86bedf44c7bd64a74ea02a0cff1de/crates/filemeta/src/filemeta/version.rs#L3482-L3518","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Heal the bucket/object to rebuild xl.meta from the remaining intact shards","Verify the xl.meta file size on each shard (ls -l on the xl-meta path across drives) to find which copies are truncated","Restore the object from backup if all copies are short","Replace/monitor the failing drive if truncation recurs on one node"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Before reading, sanity-check the stat size against the header's declared extent\nif metadata_bytes_header_len > stat_size {\n    // layout contradicts file length: truncated/corrupt xl.meta -> heal instead of read\n}","typeGuard":null,"tryCatchPattern":"match read_xl_meta_no_data(&mut reader, size).await {\n    Err(e) if matches!(e, rustfs_filemeta::error::Error::Unexpected) => {\n        // header extent exceeds the file: truncated metadata, heal the object\n    }\n    other => other?,\n}","preventionTips":["Compare xl.meta sizes across shards when investigating; unequal sizes pinpoint the truncated copy","Heal on detection — header/size disagreement never self-corrects","Track which drive served the truncated copy and check its health"],"tags":["metadata","xl-meta","truncation","io","data-corruption"],"backgroundTag":"truncated-metadata-file","analyzedSha":"9e6e02ea09c86bedf44c7bd64a74ea02a0cff1de","analyzedAt":"2026-08-16T20:34:17.560Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}