{"record":{"id":"dddd93818fff4ace","repo":"risingwavelabs/risingwave","slug":"unexpected-eof-while-decoding-meta-snapshot","errorCode":null,"errorMessage":"unexpected EOF while decoding meta snapshot","messagePattern":"unexpected EOF while decoding meta snapshot","errorType":"exception","errorClass":"BackupError","httpStatus":null,"severity":"error","filePath":"src/storage/backup/src/error.rs","lineNumber":67,"sourceCode":"        #[source]\n        BoxedError,\n    ),\n    #[error(\"Checksum mismatch: expected {expected}, found: {found}\")]\n    ChecksumMismatch { expected: u64, found: u64 },\n    #[error(\"Meta storage is not empty before being restored\")]\n    NonemptyMetaStorage,\n    #[error(transparent)]\n    Other(\n        #[from]\n        #[backtrace]\n        anyhow::Error,\n    ),\n}\n\nimpl From<std::io::Error> for BackupError {\n    fn from(err: std::io::Error) -> Self {\n        if err.kind() == std::io::ErrorKind::UnexpectedEof {\n            Self::Decoding(anyhow::anyhow!(\"unexpected EOF while decoding meta snapshot\").into())\n        } else {\n            Self::BackupStorage(err.into())\n        }\n    }\n}\n","sourceCodeStart":49,"sourceCodeEnd":73,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/storage/backup/src/error.rs#L49-L73","documentation":"BackupError::from(std::io::Error) maps an UnexpectedEof IO error to BackupError::Decoding with the message \"unexpected EOF while decoding meta snapshot\". It means the reader reached end-of-file while reading a metadata snapshot from backup storage, i.e. the snapshot file/stream is shorter than the decoder expected. The conversion exists so truncated snapshot reads surface as decoding errors rather than generic storage errors.","triggerScenarios":"Reading a meta snapshot via MetaSnapshotV1/V2 readers (decode_from_reader, read_to_end, decode_hummock_sequences_from_stream, finish) when the underlying object storage reader returns UnexpectedEof mid-decode, e.g. the snapshot object was truncated, partially uploaded, or the object reader's expected size exceeds the actual stored bytes.","commonSituations":"A backup upload was interrupted so the stored snapshot object is truncated; the wrong (older) snapshot version is fetched with a newer reader expecting more data; object storage returns fewer bytes than the manifest claims; manual tampering or a corrupted backup file.","solutions":["Re-upload or restore from a different, intact meta snapshot backup; verify the snapshot object's byte length matches expectations.","Check that the backup was fully written (compare content_length/checksum recorded at creation time against the stored object).","Verify you are decoding with the reader version matching the snapshot format version (V1 vs V2).","If reproducible, capture the object store logs / read path (ObjectDataStreamReader) and file a bug with the backup manifest."],"exampleFix":"// before: blindly decoding a snapshot path from env\nlet snap = MetaSnapshotV2::decode_from_reader(reader).await?;\n// after: validate snapshot size/known-good backup first\nassert_snapshot_intact(&manifest, &storage)?; // compares length + checksum\nlet snap = MetaSnapshotV2::decode_from_reader(reader).await?;","handlingStrategy":"validation","validationCode":"let size = storage.get_object_size(&snapshot_key).await?;\nif size < MIN_SNAPSHOT_SIZE { return Err(anyhow!(\"snapshot {} truncated ({} bytes)\", snapshot_key, size)); }\n// optionally verify recorded checksum before decoding\nverify_manifest_checksum(&manifest, &storage, &snapshot_key).await?;","typeGuard":"fn is_snapshot_intact(manifest: &BackupManifest, actual_size: u64) -> bool {\n    actual_size == manifest.snapshot_size && actual_size >= 8\n}","tryCatchPattern":"match MetaSnapshotV2::decode_from_reader(reader).await {\n    Ok(snap) => snap,\n    Err(e) if e.to_string().contains(\"unexpected EOF\") => {\n        return Err(anyhow!(\"snapshot corrupted/truncated; use another backup\"));\n    }\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Record snapshot size and checksum in the backup manifest at creation time and verify before restore.","Avoid interrupting backup uploads; use atomic/complete-then-commit object writes.","Pin reader version to the snapshot format version produced by your cluster."],"tags":["rust","backup","snapshot-decoding","eof"],"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"}