{"record":{"id":"fa03fbfecd3a5778","repo":"quickwit-oss/tantivy","slug":"failed-to-read-block-content","errorCode":null,"errorMessage":"failed to read block content","messagePattern":"failed to read block content","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"sstable/src/block_reader.rs","lineNumber":102,"sourceCode":"                        }\n                    }\n                }\n                1..=3 => {\n                    return Err(io::Error::new(\n                        io::ErrorKind::UnexpectedEof,\n                        \"failed to read block_len\",\n                    ));\n                }\n                _ => self.reader.read_u32() as usize,\n            };\n            if block_len <= 1 {\n                return Ok(false);\n            }\n            let compress = self.reader.read_u8();\n            let block_len = block_len - 1;\n\n            if self.reader.len() < block_len {\n                return Err(io::Error::new(\n                    io::ErrorKind::UnexpectedEof,\n                    \"failed to read block content\",\n                ));\n            }\n            if compress == 1 {\n                #[cfg(feature = \"zstd-compression\")]\n                {\n                    let required_capacity =\n                        Decompressor::upper_bound(&self.reader[..block_len]).unwrap_or(1024 * 1024);\n                    self.buffer.reserve(required_capacity);\n                    Decompressor::new()?\n                        .decompress_to_buffer(&self.reader[..block_len], &mut self.buffer)?;\n\n                    self.reader.advance(block_len);\n                }\n\n                if cfg!(not(feature = \"zstd-compression\")) {\n                    return Err(io::Error::new(","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/quickwit-oss/tantivy/blob/b5d8deb80c26924e6b007a5b1a7630f35ca64de4/sstable/src/block_reader.rs#L84-L120","documentation":"Thrown by read_block() after parsing the compression flag when the remaining reader length is less than the declared block_len (block length minus the 1-byte compress flag). The block claims more payload bytes than the file actually contains, so the read is aborted with UnexpectedEof — a truncated or misaligned SSTable.","triggerScenarios":"Reading an SSTable block whose declared block_len exceeds the bytes remaining in the reader; happens on truncated files, wrong offsets into the file, or blocks from an incompatible format.","commonSituations":"Incomplete index file transfers; disk corruption cutting segment files short; seeking into the middle of a block and misinterpreting payload bytes as a header.","solutions":["Validate the SSTable file size against expected/manifest size and re-copy if short","Restore the file from backup or rebuild the index","Ensure block reads start at valid offsets from the block index, not arbitrary positions","Confirm writer/reader format version compatibility"],"exampleFix":"// before\nif self.reader.len() < block_len {\n    return Err(io::Error::new(io::ErrorKind::UnexpectedEof, \"failed to read block content\"));\n}\n// after\nif self.reader.len() < block_len {\n    return Err(io::Error::new(io::ErrorKind::UnexpectedEof, format!(\"failed to read block content: need {block_len} bytes, have {}\", self.reader.len())));\n}","handlingStrategy":"validation","validationCode":"fn block_fits_in_reader(reader: &BlockReader, block_start: usize) -> bool {\n    // header: 1 byte count + 1 byte compress flag + up to 4 bytes len\n    if block_start + 2 > reader.len() { return false; }\n    let len_byte_count = reader.peek_u8_at(block_start) as usize;\n    if len_byte_count != 0 && len_byte_count != 4 { return false; }\n    let block_len = reader.peek_u32_at(block_start + 2) as usize;\n    block_start + 2 + block_len <= reader.len()\n}","typeGuard":"fn declared_block_fits(reader_len: usize, offset: usize, block_len: usize) -> bool {\n    offset + block_len <= reader_len\n}","tryCatchPattern":"match block_reader.read_block() {\n    Err(e) if e.kind() == io::ErrorKind::UnexpectedEof\n        && e.to_string() == \"failed to read block content\" => {\n        eprintln!(\"block extends past EOF; SSTable is truncated\");\n        // restore file or abort scan\n    }\n    Err(e) => return Err(e.into()),\n    Ok(done) => if !done { /* process block */ },\n}","preventionTips":["Validate SSTable file size against the manifest before opening for reads","Use atomic copy (temp file + rename) when transferring index files","Always read blocks from index-provided offsets to stay header-aligned","Monitor for disk-full events during index writes which produce short files"],"tags":["io","sstable","truncation","rust"],"backgroundTag":"truncated-block","analyzedSha":"b5d8deb80c26924e6b007a5b1a7630f35ca64de4","analyzedAt":"2026-09-05T13:20:51.521Z","contentChangedAt":"2026-09-05T13:20:51.521Z","schemaVersion":2},"datasetVersion":"2026-09-12T17:17:11.597Z"}