{"record":{"id":"53213346240f8278","repo":"quickwit-oss/tantivy","slug":"failed-to-deserialize-terminfoblockmeta","errorCode":null,"errorMessage":"Failed to deserialize terminfoblockmeta","messagePattern":"Failed to deserialize terminfoblockmeta","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"src/termdict/fst_termdict/term_info_store.rs","lineNumber":143,"sourceCode":"        let (len_slice, main_slice) = term_info_store_file.split(16);\n        let mut bytes = len_slice.read_bytes()?;\n        let len = u64::deserialize(&mut bytes)? as usize;\n        let num_terms = u64::deserialize(&mut bytes)? as usize;\n        let (block_meta_file, term_info_file) = main_slice.split(len);\n        let term_info_bytes = term_info_file.read_bytes()?;\n        Ok(TermInfoStore {\n            num_terms,\n            block_meta_bytes: block_meta_file.read_bytes()?,\n            term_info_bytes,\n        })\n    }\n\n    pub fn get(&self, term_ord: TermOrdinal) -> TermInfo {\n        let block_id = (term_ord as usize) / BLOCK_LEN;\n        let buffer = self.block_meta_bytes.as_slice();\n        let mut block_data: &[u8] = &buffer[block_id * TermInfoBlockMeta::SIZE_IN_BYTES..];\n        let term_info_block_data = TermInfoBlockMeta::deserialize(&mut block_data)\n            .expect(\"Failed to deserialize terminfoblockmeta\");\n        let inner_offset = (term_ord as usize) % BLOCK_LEN;\n        if inner_offset == 0 {\n            return term_info_block_data.ref_term_info;\n        }\n        let term_info_data = self.term_info_bytes.as_slice();\n        term_info_block_data.deserialize_term_info(\n            &term_info_data[term_info_block_data.offset as usize..],\n            inner_offset - 1,\n        )\n    }\n\n    pub fn num_terms(&self) -> usize {\n        self.num_terms\n    }\n}\n\npub struct TermInfoStoreWriter {\n    buffer_block_metas: Vec<u8>,","sourceCodeStart":125,"sourceCodeEnd":161,"githubUrl":"https://github.com/quickwit-oss/tantivy/blob/b5d8deb80c26924e6b007a5b1a7630f35ca64de4/src/termdict/fst_termdict/term_info_store.rs#L125-L161","documentation":"`TermInfoStore::get` slices the serialized block metadata at `term_ord / BLOCK_LEN` and deserializes a `TermInfoBlockMeta` with `expect(\"Failed to deserialize terminfoblockmeta\")`. Deserialization failing means the stored metadata bytes cannot be parsed — i.e. the term dictionary's block metadata on disk is corrupt, truncated, or was written by an incompatible format version.","triggerScenarios":"Calling `get(term_ord)` with any ordinal once the store's `block_meta_bytes` are corrupt/truncated; opening an index whose termdict was written by a different tantivy version with a different `TermInfoBlockMeta` wire format; or constructing the store with misaligned bytes (wrong offsets in the hotcache/segment layout).","commonSituations":"Index corruption after a crash or disk failure, copying segments incompletely (missing/truncated files), opening an index across incompatible library versions, or bit-rot on stored segment files.","solutions":["Re-index the data from the original source — corrupt term dictionary metadata cannot be repaired in place reliably.","Verify the tantivy version used to read matches the one that wrote the index; version-mismatched wire formats will not deserialize.","Restore the segment files from backup and confirm all files were copied completely (checksum/size verification).","If you control the store construction (e.g. building a hotcache), validate that `block_meta_bytes` boundaries align exactly with `TermInfoBlockMeta::SIZE_IN_BYTES` multiples."],"exampleFix":"// before: reading a segment copied with an incomplete file list\nlet store = TermInfoStore::open(partially_copied_segment);\nlet info = store.get(term_ord); // panics on truncated meta\n\n// after: validate file sizes before opening\nif block_meta_len % TermInfoBlockMeta::SIZE_IN_BYTES != 0 {\n    return Err(\"corrupt terminfo block meta: re-index\");\n}\nlet store = TermInfoStore::open(verified_segment);","handlingStrategy":"validation","validationCode":"fn block_meta_is_aligned(store_bytes: &[u8]) -> bool {\n    use tantivy::termdict::TermInfoBlockMeta;\n    !store_bytes.is_empty()\n        && store_bytes.len() % TermInfoBlockMeta::SIZE_IN_BYTES == 0\n}","typeGuard":"fn safe_get(store: &TermInfoStore, term_ord: u64, meta_len: usize) -> Option<TermInfo> {\n    let block_id = term_ord as usize / tantivy::termdict::BLOCK_LEN;\n    if (block_id + 1) * tantivy::termdict::TermInfoBlockMeta::SIZE_IN_BYTES > meta_len {\n        return None; // corrupt/truncated meta\n    }\n    Some(store.get(term_ord))\n}","tryCatchPattern":"let info = std::panic::catch_unwind(|| store.get(term_ord))\n    .map_err(|_| anyhow::anyhow!(\"corrupt terminfo block meta at ord {} — re-index\", term_ord))?;","preventionTips":["Verify segment file sizes/checksums after copying or replicating an index","Always open an index with the same tantivy version that wrote it","Run a recovery/verify pass on segments after crashy writes before serving queries"],"tags":["panic","deserialization","index-corruption","termdict"],"backgroundTag":"index-corruption","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"}