{"record":{"id":"402eeb60057d6a2d","repo":"spacejam/sled","slug":"crc-mismatch-for-read-of-batch-frame","errorCode":null,"errorMessage":"crc mismatch for read of batch frame","messagePattern":"crc mismatch for read of batch frame","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/metadata_store.rs","lineNumber":545,"sourceCode":"    reusable_frame_buffer.reserve(len + 12);\n    unsafe {\n        reusable_frame_buffer.set_len(len + 12);\n    }\n    reusable_frame_buffer[..8].copy_from_slice(&frame_size_with_crc_buf);\n\n    fallible!(file.read_exact(&mut reusable_frame_buffer[8..]));\n\n    let crc_actual = crc32fast::hash(&reusable_frame_buffer[..len + 8]) ^ 0xAF;\n    let crc_recorded = u32::from_le_bytes([\n        reusable_frame_buffer[len + 8],\n        reusable_frame_buffer[len + 9],\n        reusable_frame_buffer[len + 10],\n        reusable_frame_buffer[len + 11],\n    ]);\n\n    if crc_actual != crc_recorded {\n        log::warn!(\"encountered incorrect crc for batch in log\");\n        return Err(annotate!(io::Error::new(\n            io::ErrorKind::InvalidData,\n            \"crc mismatch for read of batch frame\",\n        )));\n    }\n\n    let mut ret = vec![];\n\n    let mut decoder = ZstdDecoder::new(&reusable_frame_buffer[8..len + 8])\n        .expect(\"failed to create zstd decoder\");\n\n    let mut object_id_buf: [u8; 8] = [0; 8];\n    let mut collection_id_buf: [u8; 8] = [0; 8];\n    let mut location_buf: [u8; 8] = [0; 8];\n    let mut low_key_len_buf: [u8; 8] = [0; 8];\n    let mut low_key_buf = vec![];\n    loop {\n        let first_read_res = decoder\n            .read_exact(&mut object_id_buf)","sourceCodeStart":527,"sourceCodeEnd":563,"githubUrl":"https://github.com/spacejam/sled/blob/e449d17111f4a097e1c66b6db241962ccb6a4136/src/metadata_store.rs#L527-L563","documentation":"After the frame length, read_frame reads the payload plus a trailing CRC and verifies it. If the recorded CRC differs from the CRC computed over the payload, the batch frame's contents are corrupt and it is rejected (after logging a warning) instead of being applied.","triggerScenarios":"Reading a metadata log/snapshot batch whose payload bytes were modified after write: torn write during a crash, bit rot, or partial disk flush.","commonSituations":"Power loss mid-write during metadata persistence; failing drives; interrupted file copies of the database directory.","solutions":["Restore metadata/log files from backup, or let the system rebuild them from other durable state if possible","Remove/rename the corrupt log segment so recovery can resynchronize from earlier valid frames (accepting data loss for that segment)","Check storage health (SMART/fsck) and fix underlying hardware issues","Recreate or re-import the database if corruption is widespread"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// ensure sufficient free space and that the log directory is writable before opening\nlet stat = nix::sys::statvfs::stat(&path)?;\nif stat.blocks_available < min_free_blocks { return Err(anyhow!(\"insufficient space for metadata writes\")); }","typeGuard":null,"tryCatchPattern":"match Db::open(&path) {\n    Ok(db) => db,\n    Err(e) if e.to_string().contains(\"crc mismatch for read of batch frame\") => {\n        // payload corrupted: restore metadata backup or rebuild segment\n        repair_or_restore_metadata(&path)?;\n        Db::open(&path)?\n    }\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Use reliable, fsync-respecting storage to avoid torn batch writes","Keep metadata backups and test restores","Avoid power-cut scenarios (UPS, clean shutdown handlers)","Verify file integrity after copying databases"],"tags":["rust","corruption","crc","metadata"],"backgroundTag":"checksum-mismatch","analyzedSha":"e449d17111f4a097e1c66b6db241962ccb6a4136","analyzedAt":"2026-09-12T01:23:10.985Z","contentChangedAt":"2026-09-12T01:23:10.985Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}