tursodatabase/turso · error

MVCC logical log header reserved bytes must be zero

Error message

MVCC logical log header reserved bytes must be zero

What it means

Header bytes 16 through 51 (MVCC_LOG_HEADER_RESERVED_START..MVCC_LOG_HEADER_CRC_START) are reserved and must be all zero. The validator rejects any log that sets them because this build assigns no meaning to those bytes and cannot interpret a header that uses them. Together with the checksum check that follows, it catches both tampering and logs from divergent builds that store data in reserved space.

Source

Thrown at cli/sync_server.rs:1003

        return Err(anyhow!("invalid MVCC logical log magic"));
    }
    if log[4] != MVCC_LOG_VERSION {
        return Err(anyhow!("unsupported MVCC logical log version {}", log[4]));
    }
    if log[5] & 0b1111_1110 != 0 {
        return Err(anyhow!("invalid MVCC logical log header flags"));
    }
    let header_len = u16::from_le_bytes([log[6], log[7]]) as usize;
    if header_len != MVCC_LOG_HEADER_SIZE {
        return Err(anyhow!(
            "invalid MVCC logical log header length: {header_len}"
        ));
    }
    if log[MVCC_LOG_HEADER_RESERVED_START..MVCC_LOG_HEADER_CRC_START]
        .iter()
        .any(|byte| *byte != 0)
    {
        return Err(anyhow!(
            "MVCC logical log header reserved bytes must be zero"
        ));
    }
    let stored_crc = read_u32_le(log, MVCC_LOG_HEADER_CRC_START)?;
    let mut crc_buf = [0u8; MVCC_LOG_HEADER_SIZE];
    crc_buf.copy_from_slice(&log[..MVCC_LOG_HEADER_SIZE]);
    crc_buf[MVCC_LOG_HEADER_CRC_START..MVCC_LOG_HEADER_SIZE].fill(0);
    let expected_crc = crc32c::crc32c(&crc_buf);
    if stored_crc != expected_crc {
        return Err(anyhow!("MVCC logical log header checksum mismatch"));
    }
    Ok(())
}

fn initial_mvcc_log_crc(log: &[u8]) -> Result<u32> {
    let salt = u64::from_le_bytes(
        log[MVCC_LOG_HEADER_SALT_START..MVCC_LOG_HEADER_SALT_END]
            .try_into()

View on GitHub (pinned to bad083fafb)

Solutions

  1. Re-pull the logical log from the producer; nonzero reserved bytes mean the header was altered or written by a different format.
  2. Verify both sync endpoints run the same Turso build; a build that populates reserved fields will always be rejected here.
  3. Hexdump bytes 16-51 and confirm they are zero to distinguish corruption from format divergence.
  4. If building test logs programmatically, zero the whole 56-byte header before filling known fields.

Example fix

// before
let snapshot = scan_mvcc_log(&log)?; // errors: reserved bytes must be zero

// after: pre-check reserved bytes and treat failure as a corrupt header
let reserved_zero = log
    .get(MVCC_LOG_HEADER_RESERVED_START..MVCC_LOG_HEADER_CRC_START)
    .is_some_and(|bytes| bytes.iter().all(|b| *b == 0));
anyhow::ensure!(reserved_zero, "MVCC log header reserved bytes nonzero; re-pull the log");
let snapshot = scan_mvcc_log(&log)?;
Defensive patterns

Strategy: validation

Validate before calling

fn mvcc_reserved_bytes_zero(log: &[u8]) -> bool {
    log.get(16..52).is_some_and(|bytes| bytes.iter().all(|b| *b == 0))
}
// run before scan_mvcc_log:
anyhow::ensure!(mvcc_reserved_bytes_zero(&log), "reserved header bytes nonzero; re-pull");

Type guard

fn mvcc_reserved_bytes_zero(log: &[u8]) -> bool {
    log.get(16..52).is_some_and(|bytes| bytes.iter().all(|b| *b == 0))
}

Try / catch

match scan_mvcc_log(&log) {
    Ok(snapshot) => { /* proceed */ }
    Err(err) if err.to_string().contains("reserved bytes must be zero") => {
        // header written by a divergent format or tampered: re-pull the log
    }
    Err(err) => return Err(err),
}

Prevention

When it happens

Trigger: scan_mvcc_log(&log) where any byte in header range [16,52) is nonzero: a fork or patched build that stashes fields in reserved space, post-write corruption that survives the magic, version, flags, and length checks, or a hand-crafted test log that forgot to zero the reserved span.

Common situations: Running a patched or forked Turso build that populates reserved header bytes; logs damaged in transit (interrupted and resumed rsync/SCP); test fixtures built by hand without zeroed reserved bytes; bit rot confined to the middle of the header.

Related errors


AI-assisted analysis of tursodatabase/turso@bad083fafb (2026-08-16). Data as JSON: /api/errors/92fe48dec41795ef. Report an issue: GitHub.