{"record":{"id":"5f05c9e5d0b8dc74","repo":"risingwavelabs/risingwave","slug":"metadata-snapshot-is-truncated-while-reading-u64","errorCode":null,"errorMessage":"metadata snapshot is truncated while reading u64","messagePattern":"metadata snapshot is truncated while reading u64","errorType":"exception","errorClass":"BackupError","httpStatus":null,"severity":"error","filePath":"src/storage/backup/src/meta_snapshot_v2.rs","lineNumber":435,"sourceCode":"        let uploader = store.streaming_upload(path).await.unwrap();\n        snapshot.encode_to_uploader(uploader).await.unwrap();\n    }\n\n    async fn snapshot_reader(store: &ObjectStoreImpl, path: &str) -> SnapshotPayloadReader {\n        SnapshotPayloadReader::new(ObjectDataStreamReader::new(\n            store.streaming_read(path, ..).await.unwrap().into_stream(),\n        ))\n    }\n\n    fn append_checksum(mut payload: Vec<u8>) -> Vec<u8> {\n        let checksum = crate::xxhash64_checksum(&payload);\n        payload.put_u64_le(checksum);\n        payload\n    }\n\n    fn read_u64_le(buf: &mut &[u8]) -> BackupResult<u64> {\n        if buf.remaining() < size_of::<u64>() {\n            return Err(BackupError::Other(anyhow!(\n                \"metadata snapshot is truncated while reading u64\"\n            )));\n        }\n        Ok(buf.get_u64_le())\n    }\n\n    fn read_len_prefix_from_slice(buf: &mut &[u8]) -> BackupResult<usize> {\n        match read_u32_le(buf)? {\n            0 => read_u64_le(buf)?.try_into().map_err(|_| {\n                BackupError::Other(anyhow!(\"metadata JSON payload length exceeds usize\"))\n            }),\n            len => Ok(len as usize),\n        }\n    }\n\n    fn skip_with_len_prefix_from_slice(buf: &mut &[u8]) -> BackupResult<()> {\n        let len = read_len_prefix_from_slice(buf)?;\n        if buf.remaining() < len {","sourceCodeStart":417,"sourceCodeEnd":453,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/storage/backup/src/meta_snapshot_v2.rs#L417-L453","documentation":"This error indicates the snapshot byte buffer had fewer than 8 bytes remaining when a little-endian u64 was required. It is a truncation guard used by the slice-based snapshot reader, signaling that the metadata snapshot payload ends mid-field and cannot be parsed.","triggerScenarios":"read_u64_le is called on a &[u8] whose remaining() < 8 — e.g. read_len_prefix_from_slice encountering the 0 marker for u64-length but the buffer ends, or reading the trailing checksum of a truncated metadata snapshot.","commonSituations":"Restoring a metadata snapshot that was cut short by a failed upload, disk-full during backup write, or manually truncated file; passing a wrong/short byte slice to the parser.","solutions":["Check the snapshot file size against the size recorded in the backup manifest; re-transfer or re-create the backup if truncated","Confirm the caller passes the complete payload slice, not a partial read","Add/treat this as a corruption signal: fail the restore early instead of retrying parsing"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"fn has_u64(buf: &[u8]) -> bool { buf.remaining() >= std::mem::size_of::<u64>() }","typeGuard":null,"tryCatchPattern":"match parse_snapshot(buf) {\n    Err(BackupError::Other(e)) if format!(\"{e:#}\").contains(\"truncated\") => {\n        // treat as corrupted/truncated snapshot: fail restore, prompt re-backup\n    }\n    other => other?,\n}","preventionTips":["Validate file size against the manifest before parsing","Never hand-truncate or slice snapshot payloads at arbitrary offsets","Use length-checksummed framing when transferring snapshots"],"tags":["rust","backup","truncation","serialization"],"backgroundTag":"unexpected-response-shape","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}