quickwit-oss/tantivy · error · io::Error
UnexpectedEof
UnexpectedEof
Error message
Failed to deserialize column index. Empty buffer.
What it means
open_column_index deserializes a serialized column index; the first byte encodes the cardinality. An empty buffer cannot even supply that byte, so the function returns UnexpectedEof with this message. It typically means truncated or missing columnar data.
Source
Thrown at columnar/src/column_index/serialize.rs:72
SerializableColumnIndex::Optional(SerializableOptionalIndex {
non_null_row_ids,
num_rows,
}) => serialize_optional_index(non_null_row_ids.as_ref(), num_rows, &mut output)?,
SerializableColumnIndex::Multivalued(multivalued_index) => {
serialize_multivalued_index(&multivalued_index, &mut output)?
}
}
let column_index_num_bytes = output.written_bytes() as u32;
Ok(column_index_num_bytes)
}
/// Open a serialized column index.
pub fn open_column_index(
mut bytes: OwnedBytes,
format_version: Version,
) -> io::Result<ColumnIndex> {
if bytes.is_empty() {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"Failed to deserialize column index. Empty buffer.",
));
}
let cardinality_code = bytes[0];
let cardinality = Cardinality::try_from_code(cardinality_code)?;
bytes.advance(1);
match cardinality {
Cardinality::Full => Ok(ColumnIndex::Full),
Cardinality::Optional => {
let optional_index = super::optional_index::open_optional_index(bytes)?;
Ok(ColumnIndex::Optional(optional_index))
}
Cardinality::Multivalued => {
let multivalue_index =
super::multivalued_index::open_multivalued_index(bytes, format_version)?;
Ok(ColumnIndex::Multivalued(multivalue_index))
}View on GitHub (pinned to b5d8deb80c)
Solutions
- Check that the segment file is complete and not truncated; re-copy or re-index.
- Verify offsets/lengths used to slice the column index bytes come from the correct footer/meta.
- Handle empty buffers before calling: treat as missing column and fall back to a non-indexed access path.
- Rebuild the index if corruption is confirmed.
Example fix
// before
let idx = open_column_index(bytes, version)?; // UnexpectedEof if empty
// after
if bytes.is_empty() {
return Ok(ColumnIndex::empty()); // or skip column
}
let idx = open_column_index(bytes, version)?; Defensive patterns
Strategy: validation
Validate before calling
fn open_column_index_safe(bytes: OwnedBytes, version: Version) -> io::Result<Option<ColumnIndex>> {
if bytes.is_empty() {
return Ok(None); // column index absent
}
open_column_index(bytes, version).map(Some)
} Try / catch
match open_column_index(bytes, version) {
Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => {
// fall back to unindexed column access or rebuild segment
}
other => other.map(Some),
} Prevention
- Ensure commits complete before reading segments (check for crash-incomplete segments).
- Validate file sizes against the segment meta/footer before slicing byte ranges.
- Handle empty/missing column regions explicitly instead of passing empty buffers.
- Re-download or re-index damaged index directories.
When it happens
Trigger: Passing an empty OwnedBytes to open_column_index — e.g. reading a zero-length column-index region from a truncated segment, a failed mmap, or wrong offset/length when loading columnar files.
Common situations: Incomplete segment writes (crash during commit), corrupted downloads/copies of index directories, misaligned byte-range reads, opening columns that were never written.
Related errors
AI-assisted analysis of quickwit-oss/tantivy@b5d8deb80c (2026-09-05).
Data as JSON: /api/errors/ea7a5b9b357960fc.
Report an issue: GitHub.