{"record":{"id":"f997ec98dd9b438c","repo":"quickwit-oss/quickwit","slug":"unsupported-split-recovery-metadata-format-version-f997ec","errorCode":null,"errorMessage":"unsupported split recovery metadata format version: {version} (at line 70; candidate line 75 wraps a dynamic protobuf decode error as io::Error InvalidData)","messagePattern":"unsupported split recovery metadata format version: (.+?) \\(at line 70; candidate line 75 wraps a dynamic protobuf decode error as io::Error InvalidData\\)","errorType":"exception","errorClass":"io::Error (InvalidData)","httpStatus":null,"severity":"error","filePath":"quickwit/quickwit-proto/src/metastore/mod.rs","lineNumber":75,"sourceCode":"        use prost::Message;\n\n        if bytes.len() < SPLIT_RECOVERY_METADATA_HEADER_LEN\n            || &bytes[..SPLIT_RECOVERY_METADATA_MAGIC.len()] != SPLIT_RECOVERY_METADATA_MAGIC\n        {\n            return Err(io::Error::new(\n                io::ErrorKind::InvalidData,\n                \"invalid split recovery metadata magic number\",\n            ));\n        }\n        let version = bytes[SPLIT_RECOVERY_METADATA_MAGIC.len()];\n        if version != SPLIT_RECOVERY_METADATA_FORMAT_VERSION {\n            return Err(io::Error::new(\n                io::ErrorKind::InvalidData,\n                format!(\"unsupported split recovery metadata format version: {version}\"),\n            ));\n        }\n        bytes = &bytes[SPLIT_RECOVERY_METADATA_HEADER_LEN..];\n        Self::decode(bytes).map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))\n    }\n}\n\n#[cfg(test)]\nmod split_recovery_metadata_tests {\n    use super::SplitRecoveryMetadata;\n    use crate::types::{DocMappingUid, IndexUid};\n\n    #[test]\n    fn test_split_recovery_metadata_roundtrip_and_unknown_fields() {\n        let metadata = SplitRecoveryMetadata {\n            split_id: \"split-a\".to_string(),\n            index_uid: Some(IndexUid::for_test(\"index-a\", 1)),\n            source_id: \"source-a\".to_string(),\n            node_id: \"node-a\".to_string(),\n            doc_mapping_uid: Some(DocMappingUid::for_test(2)),\n            partition_id: 3,\n            num_docs: 4,","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/quickwit-oss/quickwit/blob/a39730c5cdcd1a4fe798403737ae293999ea21f8/quickwit/quickwit-proto/src/metastore/mod.rs#L57-L93","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"fn payload_plausible(bytes: &[u8]) -> bool {\n    bytes.len() > SPLIT_RECOVERY_METADATA_HEADER_LEN // has protobuf body\n}","typeGuard":null,"tryCatchPattern":"match SplitRecoveryMetadata::deserialize(&bytes) {\n    Err(e) if e.kind() == std::io::ErrorKind::InvalidData => {\n    // inspect e.source() for the prost DecodeError to find the corrupt field\n    regenerate_metadata()\n}\n    other => other?,\n}","preventionTips":["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."],"tags":["io","protobuf","serialization","invalid-data"],"backgroundTag":"protobuf-unmarshal-failed","analyzedSha":"a39730c5cdcd1a4fe798403737ae293999ea21f8","analyzedAt":"2026-09-08T13:19:37.784Z","contentChangedAt":"2026-09-08T13:19:37.784Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}