databendlabs/databend · error · InvalidData
Fst data is corrupted
Error message
Fst data is corrupted: {:?} What it means
custom_search_impl builds Fst maps from stored column data; if Fst::new fails to parse the stored FST bytes it wraps the error in an io::Error(InvalidData) reading 'Fst data is corrupted: {:?}'. This means the persisted FST (finite-state transducer) for an inverted-index field is unreadable — truncated, empty-but-marked-present, or written by an incompatible fst version. The search via search() cannot proceed for that field.
Solutions
- Verify the fst-{field_id} column blob is complete (compare against expected size / re-read from storage)
- Rebuild the inverted index for the affected fields so the FST is regenerated
- Check that reader and writer use compatible fst crate versions / index format versions
- Restore the segment/index files from snapshot or backup
Example fix
// before: assuming present blob is valid
let fst = Fst::new(fst_data).map_err(|err| io::Error::new(ErrorKind::InvalidData, format!("Fst data is corrupted: {:?}", err)))?;
// after: treat empty/corrupt as absent and fall back
let fst = Fst::new(fst_data).unwrap_or_else(|_| Fst::from_iter_str(vec![String::new()])); // empty FST fallback Defensive patterns
Strategy: try-catch
Validate before calling
// Fst::new validates magic/version; pre-check presence and non-emptiness
if fst_blob.is_empty() { return Err("fst column is empty; index needs rebuild"); } Type guard
fn fst_data_ok(b: &[u8]) -> bool { !b.is_empty() && b.len() >= 8 } Try / catch
let fst = match Fst::new(fst_data) {
Ok(f) => f,
Err(err) => {
log::warn!("fst-{} corrupted: {:?}; rebuilding index", field_id, err);
rebuild_inverted_index(field_id).await?
}
}; Prevention
- Snapshot files only after index writes are fsynced and complete
- Record FST sizes/checksums in the index meta and verify before Fst::new
- Keep fst crate versions consistent between index writers and readers
- Schedule periodic index verification/rebuild jobs
When it happens
Trigger: Inverted-index search where the column file named 'fst-{field_id}' exists but its bytes fail Fst::new validation (bad magic/version/checksum, truncation, empty vector).
Common situations: Partially written index segments after a crash; corrupted snapshot files; index format version skew between writer and reader; manual file truncation during copy.
Understand the failure class
Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.
Related errors
- Expected to ignore a byte
- Expected to have bytes
- Expected to have insensitive bytes
- Must reach the buffer end
- Unsupported format for
AI-assisted analysis of databendlabs/databend@288d84d76e (2026-09-11).
Data as JSON: /api/errors/6e358493e528f3d6.
Report an issue: GitHub.
Appendix: source
Thrown at src/query/storages/fuse/src/io/read/inverted_index/inverted_index_reader.rs:277
if let Some(term_col_meta) = inverted_index_meta_map.remove(&term_col_name) {
let term_range = term_col_meta.offset..(term_col_meta.offset + term_col_meta.len);
columns.push((term_col_name, term_range));
}
}
let column_files =
legacy_load_inverted_index_files(settings, columns, index_path, &self.dal).await?;
let mut column_files_map = column_files
.into_iter()
.map(|f| (f.name.clone(), f.data.clone()))
.collect::<HashMap<_, _>>();
let mut fst_maps = HashMap::with_capacity(field_ids.len());
for field_id in field_ids {
let fst_col_name = format!("fst-{}", field_id);
let fst = if let Some(fst_data) = column_files_map.remove(&fst_col_name) {
Fst::new(fst_data).map_err(|err| {
std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("Fst data is corrupted: {:?}", err),
)
})?
} else {
// If the FST data does not exist, create an empty FST.
// This means that the field does not have any valid terms.
let builder = tantivy_fst::MapBuilder::memory();
let bytes = builder.into_inner().unwrap();
let fst_data = OwnedBytes::new(bytes);
Fst::new(fst_data).unwrap()
};
let fst_map = tantivy_fst::Map::from(fst);
fst_maps.insert(*field_id, fst_map);
}
// 2. check whether query is matched in the fsts.
let mut matched_terms = HashMap::new();View on GitHub (pinned to 288d84d76e)