tursodatabase/turso · error
invalid MVCC logical log header length
Error message
invalid MVCC logical log header length: {header_len} What it means
Bytes 6..8 of the MVCC logical log header hold a little-endian u16 header length that must equal MVCC_LOG_HEADER_SIZE (56). The check exists so the parser can locate the salt and CRC fields reliably; any other value means the header layout is not the one this build knows how to read.
Solutions
- Restore the log from backup or regenerate it - a mismatched header length is not recoverable in place
- Match the tursodb version that produced the file if the log came from another build
- If forking the format, keep header_len in sync with MVCC_LOG_HEADER_SIZE and bump MVCC_LOG_VERSION together
Defensive patterns
Strategy: validation
Validate before calling
fn mvcc_header_length_ok(log: &[u8]) -> bool {
log.len() >= 8
&& u16::from_le_bytes([log[6], log[7]]) as usize == MVCC_LOG_HEADER_SIZE // 56
} Try / catch
let snapshot = match scan_mvcc_log(&log) {
Ok(s) => s,
Err(err) if err.to_string().starts_with("invalid MVCC logical log header length") => {
// layout mismatch: regenerate log, do not attempt partial reads
return rebuild_log_and_fallback();
}
Err(err) => return Err(err),
}; Prevention
- Treat header length as part of the format contract: change it only with a version bump
- Verify the header length field in writer unit tests
- Do not parse logs produced by unknown builds
When it happens
Trigger: validate_mvcc_log_header computes u16::from_le_bytes([log[6], log[7]]) and finds a value != 56 while serving a logical /pull-updates request.
Common situations: A future format that grew the header; a header written in big-endian by mistake in a fork; garbage bytes from corruption or a partially written header after a crash during log creation.
Related errors
- invalid MVCC logical log header flags
- MVCC logical log header reserved bytes must be zero
- invalid MVCC logical log frame end magic at offset
- invalid MVCC logical log frame magic at offset
- MVCC logical log extension block missing flag at offset
AI-assisted analysis of tursodatabase/turso@492c4a71cd (2026-08-20).
Data as JSON: /api/errors/fbc1bb1637e1c611.
Report an issue: GitHub.
Appendix: source
Thrown at cli/sync_server.rs:1111
fn is_nonportable_mvcc_log_error(err: &anyhow::Error) -> bool {
let message = err.to_string();
message.starts_with("unsupported MVCC logical log version ")
}
fn validate_mvcc_log_header(log: &[u8]) -> Result<()> {
if read_u32_le(log, 0)? != MVCC_LOG_MAGIC {
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"));
}View on GitHub (pinned to 492c4a71cd)