{"record":{"id":"acbc668844dab7e2","repo":"spacejam/sled","slug":"corrupt-frame-length","errorCode":null,"errorMessage":"corrupt frame length","messagePattern":"corrupt frame length","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/metadata_store.rs","lineNumber":517,"sourceCode":"    reusable_frame_buffer: &mut Vec<u8>,\n) -> io::Result<Vec<UpdateMetadata>> {\n    let mut frame_size_with_crc_buf: [u8; 8] = [0; 8];\n    // TODO only break if UnexpectedEof, otherwise propagate\n    fallible!(file.read_exact(&mut frame_size_with_crc_buf));\n\n    let expected_len_hash_buf =\n        [frame_size_with_crc_buf[6], frame_size_with_crc_buf[7]];\n\n    let actual_len_hash_buf: [u8; 2] =\n        (crc32fast::hash(&frame_size_with_crc_buf[..6]) as u16).to_le_bytes();\n\n    // clear crc bytes before turning into usize\n    let mut frame_size_buf = frame_size_with_crc_buf;\n    frame_size_buf[6] = 0;\n    frame_size_buf[7] = 0;\n\n    if actual_len_hash_buf != expected_len_hash_buf {\n        return Err(annotate!(io::Error::new(\n            io::ErrorKind::InvalidData,\n            \"corrupt frame length\"\n        )));\n    }\n\n    let len_u64: u64 = u64::from_le_bytes(frame_size_buf);\n    let len: usize = usize::try_from(len_u64).unwrap();\n\n    reusable_frame_buffer.clear();\n    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;","sourceCodeStart":499,"sourceCodeEnd":535,"githubUrl":"https://github.com/spacejam/sled/blob/e449d17111f4a097e1c66b6db241962ccb6a4136/src/metadata_store.rs#L499-L535","documentation":"read_frame reads an 8-byte frame length with its own checksum; the two high bytes are masked out before conversion to u64, and the stored length hash must match the recomputed hash. A mismatch means the length header is corrupt, so the frame cannot be trusted or sized, and the read fails.","triggerScenarios":"Reading a metadata log/snapshot frame whose length bytes were corrupted by torn writes, power loss, bit rot, or a misaligned read offset (e.g. after a previously corrupted frame desynchronized the log).","commonSituations":"Crash recovery replaying the metadata log; damaged snapshot files; failing disk sectors in the metadata region.","solutions":["Restore the metadata files from backup or delete them to force a rebuild if the data is recoverable elsewhere","Check for preceding corruption that desynchronized frame alignment and rescan from a known-good offset","Run disk/filesystem health checks and replace failing storage","If data is expendable, recreate the database"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// validate metadata log files exist and are non-empty before opening\nfor f in [\"blob\", \"logs\"] { let p = path.join(f); if !p.exists() { return Err(anyhow!(\"missing metadata: {}\", f)); } }","typeGuard":null,"tryCatchPattern":"match Db::open(&path) {\n    Ok(db) => db,\n    Err(e) if e.to_string().contains(\"corrupt frame length\") => {\n        // truncate/restore corrupt metadata segment, then retry\n        repair_or_restore_metadata(&path)?;\n        Db::open(&path)?\n    }\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Back up the metadata store regularly","Ensure clean shutdowns so frames are fully written","Monitor storage health; frame-length corruption usually means bit rot or torn writes","Avoid moving db files between systems without checksum verification"],"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"}