quickwit-oss/tantivy · error · io::Error
failed to read block_len
Error message
failed to read block_len
What it means
Thrown by the SSTable block reader's read_block() when the encoded block_len field is 1..=3 bytes. A legal block length prefix is either 0 (special/last marker) or 4 bytes; a 1-3 byte length is structurally impossible, so the reader reports UnexpectedEof — the stream is misaligned or truncated.
Source
Thrown at sstable/src/block_reader.rs:88
self.buffer.clear();
loop {
let block_len = match self.reader.len() {
0 => {
// we are out of data for this block. Check if we have another block after
match self.next_readers.next() {
Some((new_reader, first_ordinal)) => {
self.reader = new_reader;
self.pending_first_ordinal = Some(first_ordinal);
continue;
}
_ => {
return Ok(false);
}
}
}
1..=3 => {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"failed to read block_len",
));
}
_ => self.reader.read_u32() as usize,
};
if block_len <= 1 {
return Ok(false);
}
let compress = self.reader.read_u8();
let block_len = block_len - 1;
if self.reader.len() < block_len {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"failed to read block content",
));
}View on GitHub (pinned to b5d8deb80c)
Solutions
- Verify the SSTable file is complete and uncorrupted (compare size/checksum with the source)
- Rebuild the index or restore the SSTable from backup
- Ensure the reader starts at a valid block boundary offset (e.g. from the index/dictionary) rather than an arbitrary position
- Check writer/reader format version compatibility
Example fix
// before
match self.reader.read_bytes(1)? {
0 => /* ... */,
1..=3 => return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "failed to read block_len")),
_ => self.reader.read_u32() as usize,
}
// after
match self.reader.read_bytes(1)? {
0 => /* ... */,
1..=3 => return Err(io::Error::new(io::ErrorKind::UnexpectedEof, format!("failed to read block_len: invalid length prefix at offset {}", self.reader.pos()))),
_ => self.reader.read_u32() as usize,
} Defensive patterns
Strategy: validation
Validate before calling
fn sstable_offset_is_block_aligned(reader: &BlockReader, offset: usize) -> bool {
// a valid block starts with a length prefix of 0 or 4
if offset >= reader.len() { return false; }
match reader.peek_u8_at(offset) {
Some(0) | Some(4) => true,
_ => false,
}
} Type guard
fn has_valid_block_len_prefix(byte: u8) -> bool {
byte == 0 || byte == 4
} Try / catch
match block_reader.read_block() {
Err(e) if e.kind() == io::ErrorKind::UnexpectedEof
&& e.to_string() == "failed to read block_len" => {
eprintln!("SSTable truncated or offset misaligned; restoring from backup");
}
Err(e) => return Err(e.into()),
Ok(done) => if !done { /* process block */ },
} Prevention
- Only seek to block offsets obtained from the SSTable index, never arbitrary positions
- Verify file completeness after copies; truncated files fail at the first bad prefix
- Use matching SSTable format versions for writer and reader
- Detect truncation early by validating total file size against expected metadata
When it happens
Trigger: Reading an SSTable stream where the next block_len byte count lands on 1..=3; this occurs with truncated files or when the read offset drifts into the middle of a length prefix due to prior misparses.
Common situations: Corrupted or truncated SSTable files; reading an SSTable written by an incompatible format version; incorrect seek/offset handling by the caller leading to mid-token reads.
Related errors
- failed to read block content
- doc store block not completely decompressed, data corruption
- File corrupted. The file is smaller than Footer::SIZE_IN_BYT
- SSTable corruption
- InvalidData
AI-assisted analysis of quickwit-oss/tantivy@b5d8deb80c (2026-09-05).
Data as JSON: /api/errors/bd39cb2ff72e9448.
Report an issue: GitHub.