quickwit-oss/quickwit · error · io::Error (InvalidData)
unsupported split recovery metadata format version: {version
Error message
unsupported split recovery metadata format version: {version} (at line 70; candidate line 75 wraps a dynamic protobuf decode error as io::Error InvalidData) What it means
The final step of `SplitRecoveryMetadata::deserialize` decodes the remaining bytes with prost (`Self::decode`); any protobuf failure is converted into an io::Error of kind InvalidData via map_err. This means corrupt, truncated, or non-protobuf payloads that pass the magic/version checks surface as a wrapped prost DecodeError rather than the more specific version/magic errors.
Source
Thrown at quickwit/quickwit-proto/src/metastore/mod.rs:75
use prost::Message;
if bytes.len() < SPLIT_RECOVERY_METADATA_HEADER_LEN
|| &bytes[..SPLIT_RECOVERY_METADATA_MAGIC.len()] != SPLIT_RECOVERY_METADATA_MAGIC
{
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"invalid split recovery metadata magic number",
));
}
let version = bytes[SPLIT_RECOVERY_METADATA_MAGIC.len()];
if version != SPLIT_RECOVERY_METADATA_FORMAT_VERSION {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("unsupported split recovery metadata format version: {version}"),
));
}
bytes = &bytes[SPLIT_RECOVERY_METADATA_HEADER_LEN..];
Self::decode(bytes).map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))
}
}
#[cfg(test)]
mod split_recovery_metadata_tests {
use super::SplitRecoveryMetadata;
use crate::types::{DocMappingUid, IndexUid};
#[test]
fn test_split_recovery_metadata_roundtrip_and_unknown_fields() {
let metadata = SplitRecoveryMetadata {
split_id: "split-a".to_string(),
index_uid: Some(IndexUid::for_test("index-a", 1)),
source_id: "source-a".to_string(),
node_id: "node-a".to_string(),
doc_mapping_uid: Some(DocMappingUid::for_test(2)),
partition_id: 3,
num_docs: 4,View on GitHub (pinned to a39730c5cd)
Solutions
- Delete and regenerate the corrupted recovery metadata blob.
- Verify the byte length matches what the writer recorded (header + protobuf body).
- Check the writer path for partial-write bugs (missing fsync/atomic rename).
- Unwrap the inner prost DecodeError (it is chained as the io error source) to pinpoint the offending field.
Defensive patterns
Strategy: try-catch
Validate before calling
fn payload_plausible(bytes: &[u8]) -> bool {
bytes.len() > SPLIT_RECOVERY_METADATA_HEADER_LEN // has protobuf body
} Try / catch
match SplitRecoveryMetadata::deserialize(&bytes) {
Err(e) if e.kind() == std::io::ErrorKind::InvalidData => {
// inspect e.source() for the prost DecodeError to find the corrupt field
regenerate_metadata()
}
other => other?,
} Prevention
- Write metadata atomically (temp file + rename) to avoid partial blobs.
- Store checksums with serialized blobs and verify before deserializing.
- Keep writer and reader on identical quickwit-proto schemas.
When it happens
Trigger: Calling deserialize on bytes with a valid magic header and version byte but whose protobuf payload is corrupt: bit rot, truncation after the header, or garbage appended/mixed into the blob.
Common situations: Partially written recovery metadata from a crashed writer; manual file edits; a storage layer returning wrong or concatenated blobs; nondeterministic payloads (different protobuf schema) despite matching header.
Understand the failure class
Background: "cannot parse invalid wire-format data", "cannot unmarshal", "failed unmarshalling": protobuf unmarshal errors explained — this error's family across 10 libraries.
Related errors
- invalid split recovery metadata magic number
- dynamic protobuf decode error wrapped as io::Error (InvalidD
- unsupported split recovery metadata format version: {version
- unsupported split fields format version: {version_byte[0]}
- `doc_batch` should not be empty
AI-assisted analysis of quickwit-oss/quickwit@a39730c5cd (2026-09-08).
Data as JSON: /api/errors/f997ec98dd9b438c.
Report an issue: GitHub.