spacejam/sled · error

corrupt frame length

Error message

corrupt frame length

What it means

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.

Solutions

  1. Restore the metadata files from backup or delete them to force a rebuild if the data is recoverable elsewhere
  2. Check for preceding corruption that desynchronized frame alignment and rescan from a known-good offset
  3. Run disk/filesystem health checks and replace failing storage
  4. If data is expendable, recreate the database
Defensive patterns

Strategy: try-catch

Validate before calling

// validate metadata log files exist and are non-empty before opening
for f in ["blob", "logs"] { let p = path.join(f); if !p.exists() { return Err(anyhow!("missing metadata: {}", f)); } }

Try / catch

match Db::open(&path) {
    Ok(db) => db,
    Err(e) if e.to_string().contains("corrupt frame length") => {
        // truncate/restore corrupt metadata segment, then retry
        repair_or_restore_metadata(&path)?;
        Db::open(&path)?
    }
    Err(e) => return Err(e.into()),
}

Prevention

When it happens

Trigger: 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).

Common situations: Crash recovery replaying the metadata log; damaged snapshot files; failing disk sectors in the metadata region.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


AI-assisted analysis of spacejam/sled@e449d17111 (2026-09-12). Data as JSON: /api/errors/acbc668844dab7e2. Report an issue: GitHub.

Appendix: source

Thrown at src/metadata_store.rs:517

    reusable_frame_buffer: &mut Vec<u8>,
) -> io::Result<Vec<UpdateMetadata>> {
    let mut frame_size_with_crc_buf: [u8; 8] = [0; 8];
    // TODO only break if UnexpectedEof, otherwise propagate
    fallible!(file.read_exact(&mut frame_size_with_crc_buf));

    let expected_len_hash_buf =
        [frame_size_with_crc_buf[6], frame_size_with_crc_buf[7]];

    let actual_len_hash_buf: [u8; 2] =
        (crc32fast::hash(&frame_size_with_crc_buf[..6]) as u16).to_le_bytes();

    // clear crc bytes before turning into usize
    let mut frame_size_buf = frame_size_with_crc_buf;
    frame_size_buf[6] = 0;
    frame_size_buf[7] = 0;

    if actual_len_hash_buf != expected_len_hash_buf {
        return Err(annotate!(io::Error::new(
            io::ErrorKind::InvalidData,
            "corrupt frame length"
        )));
    }

    let len_u64: u64 = u64::from_le_bytes(frame_size_buf);
    let len: usize = usize::try_from(len_u64).unwrap();

    reusable_frame_buffer.clear();
    reusable_frame_buffer.reserve(len + 12);
    unsafe {
        reusable_frame_buffer.set_len(len + 12);
    }
    reusable_frame_buffer[..8].copy_from_slice(&frame_size_with_crc_buf);

    fallible!(file.read_exact(&mut reusable_frame_buffer[8..]));

    let crc_actual = crc32fast::hash(&reusable_frame_buffer[..len + 8]) ^ 0xAF;

View on GitHub (pinned to e449d17111)