quickwit-oss/quickwit · error · io::Error (InvalidData)

invalid split recovery metadata magic number

Error message

invalid split recovery metadata magic number

What it means

`SplitRecoveryMetadata::deserialize` expects a byte blob starting with a fixed magic header (SPLIT_RECOVERY_METADATA_MAGIC) of at least header length. If the bytes are too short or the magic does not match, the data is not split recovery metadata in the expected envelope format, so it fails with InvalidData.

Source

Thrown at quickwit/quickwit-proto/src/metastore/mod.rs:62

        use prost::Message;

        let mut output =
            Vec::with_capacity(SPLIT_RECOVERY_METADATA_HEADER_LEN + self.encoded_len());
        output.extend_from_slice(SPLIT_RECOVERY_METADATA_MAGIC);
        output.push(SPLIT_RECOVERY_METADATA_FORMAT_VERSION);
        self.encode(&mut output)
            .expect("encoding a protobuf into a Vec should not fail");
        output
    }

    /// Deserializes split recovery metadata embedded in a split bundle.
    pub fn deserialize(mut bytes: &[u8]) -> io::Result<Self> {
        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 {

View on GitHub (pinned to a39730c5cd)

Solutions

  1. Confirm the bytes being passed actually come from SplitRecoveryMetadata::serialize.
  2. Delete or re-generate stale recovery metadata written by an older Quickwit version.
  3. Check the code path that produced the blob for offset/concatenation mistakes.
  4. Use matching Quickwit versions across the cluster so all nodes write the same envelope format.
Defensive patterns

Strategy: validation

Validate before calling

fn looks_like_recovery_metadata(bytes: &[u8]) -> bool {
    bytes.len() > 5 && &bytes[..5] == b"QWSRM" // header length >= magic len
}

Try / catch

match SplitRecoveryMetadata::deserialize(&bytes) {
    Ok(meta) => handle(meta),
    Err(e) if e.kind() == std::io::ErrorKind::InvalidData => {
        // legacy or foreign payload: regenerate or discard
        log::warn!("bad recovery metadata envelope: {e}");
    }
    Err(e) => return Err(e.into()),
}

Prevention

When it happens

Trigger: Calling SplitRecoveryMetadata::deserialize on bytes produced by an older format (no magic header), on entirely unrelated bytes, or on a file read at the wrong offset.

Common situations: Upgrading Quickwit over a metastore/object store that still holds pre-magic-format blobs; hand-editing or truncating recovery metadata files; deserializing the wrong payload from a queue or file.

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


AI-assisted analysis of quickwit-oss/quickwit@a39730c5cd (2026-09-08). Data as JSON: /api/errors/f51803008d3452ea. Report an issue: GitHub.