{"record":{"id":"2f563a30944743f4","repo":"risingwavelabs/risingwave","slug":"meta-snapshot-is-missing-checksum","errorCode":null,"errorMessage":"meta snapshot is missing checksum","messagePattern":"meta snapshot is missing checksum","errorType":"exception","errorClass":"BackupError","httpStatus":null,"severity":"error","filePath":"src/storage/backup/src/meta_snapshot.rs","lineNumber":183,"sourceCode":"\n    pub async fn skip_exact(&mut self, mut len: usize) -> BackupResult<()> {\n        const BUFFER_SIZE: usize = 1024;\n        let mut buf = [0; BUFFER_SIZE];\n        while len > 0 {\n            let read_len = len.min(BUFFER_SIZE);\n            self.reader.read_exact(&mut buf[..read_len]).await?;\n            self.hasher.write(&buf[..read_len]);\n            len -= read_len;\n        }\n        Ok(())\n    }\n\n    pub async fn read_to_end(mut self) -> BackupResult<Vec<u8>> {\n        let mut data = vec![];\n        self.reader.read_to_end(&mut data).await?;\n        if data.len() < size_of::<u64>() {\n            return Err(BackupError::Decoding(\n                anyhow::anyhow!(\"meta snapshot is missing checksum\").into(),\n            ));\n        }\n\n        let checksum = data.split_off(data.len() - size_of::<u64>());\n        self.hasher.write(&data);\n        self.verify_checksum(&checksum)?;\n        Ok(data)\n    }\n\n    pub async fn finish_after_skipping_to_end(self) -> BackupResult<()> {\n        let Self { reader, mut hasher } = self;\n        let mut bytes_stream = reader.into_bytes_stream();\n        let mut tail = BytesMut::with_capacity(size_of::<u64>());\n\n        while let Some(bytes) = bytes_stream.next().await.transpose()? {\n            let payload_len = tail.len() + bytes.len();\n            if payload_len <= size_of::<u64>() {\n                tail.extend_from_slice(&bytes);","sourceCodeStart":165,"sourceCodeEnd":201,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/storage/backup/src/meta_snapshot.rs#L165-L201","documentation":"MetaSnapshot::read_to_end reads the whole snapshot payload and requires at least 8 trailing bytes for the u64 checksum; if data.len() < size_of::<u64>() it throws BackupError::Decoding(\"meta snapshot is missing checksum\"). It indicates the snapshot stream is too short to even contain the trailing checksum field, i.e. severely truncated or not a meta snapshot at all.","triggerScenarios":"Calling read_to_end() on a MetaSnapshotV1/V2 whose underlying reader yields fewer than 8 bytes total (empty or near-empty object, wrong key fetched, truncated upload).","commonSituations":"Fetching the wrong object key from backup storage; an empty snapshot object created by a failed backup job; a file replaced/truncated on disk in local backup testing; restoring a v2 snapshot from an empty S3 prefix.","solutions":["Verify the snapshot object exists and is non-empty (check object size in backup storage) and re-run restore against a complete backup.","Confirm the snapshot key/path points at the meta snapshot object, not a manifest or other artifact.","Re-create the backup with `risectl meta snapshot` (or the backup service) and validate the stored size before restoring.","If the stream is intentionally short in tests, seed it with a payload that includes an 8-byte checksum tail."],"exampleFix":"// before: decoding whatever the reader returns\nlet snap = MetaSnapshotV2::read_to_end().await?;\n// after: sanity-check object size first\nlet size = storage.get_object_size(&key).await?;\nif size < 8 { return Err(anyhow!(\"snapshot object {} truncated\", key)); }\nlet snap = MetaSnapshotV2::read_to_end().await?;","handlingStrategy":"validation","validationCode":"let size = storage.get_object_size(&key).await?;\nif size < 8 {\n    return Err(anyhow!(\"snapshot object {} too small ({} bytes) to contain a checksum\", key, size));\n}","typeGuard":"fn has_checksum_tail(data: &[u8]) -> bool { data.len() >= std::mem::size_of::<u64>() }","tryCatchPattern":"match snapshot.read_to_end().await {\n    Ok(data) => data,\n    Err(e) if e.to_string().contains(\"missing checksum\") =>\n        return Err(anyhow!(\"snapshot object empty/truncated; pick a valid backup\")),\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Always fetch snapshots via their manifest key, never a guessed path.","Check object content_length > 8 before attempting decode.","Validate backups periodically with a dry-run decode."],"tags":["rust","backup","snapshot-decoding","truncated-data"],"backgroundTag":"checksum-mismatch","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}