tursodatabase/turso · error
unsupported MVCC logical log version {}
Error message
unsupported MVCC logical log version {} What it means
Header byte 4 is the log format version and this server only reads MVCC_LOG_VERSION 3. A log written by a newer or older engine build fails this check. Notably this exact message is matched by is_nonportable_mvcc_log_error, so within handle_logical_pull_updates it is caught and downgraded to a replace-base page response — clients normally see a pages fallback, not this 500. It escapes as a hard error only through other callers of scan_mvcc_log.
Source
Thrown at cli/sync_server.rs:988
}
Ok(MvccLogSnapshot {
end_offset: offset as u64,
crc_by_offset,
})
}
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"
));
}View on GitHub (pinned to bad083fafb)
Solutions
- Rebuild/reinstall the binary that created the .db and the sync server from the same source revision.
- Delete the old-version .db-log: the server automatically falls back to replace-base pages and clients re-sync.
- Upgrade the server to a build that supports the newer log version.
Defensive patterns
Strategy: validation
Validate before calling
fn log_version(path: &std::path::Path) -> Option<u8> {
let mut buf = [0u8; 5];
let mut f = std::fs::File::open(path).ok()?;
f.read_exact(&mut buf).ok()?;
(u32::from_le_bytes(buf[..4].try_into().unwrap()) == 0x4C4D4C32).then_some(buf[4])
}
// expect Some(3) for this server; other versions trigger replace-base fallback Try / catch
The server already downgrades this error to a replace-base page response inside handle_logical_pull_updates, so clients usually see pages. If it surfaces as a 500 elsewhere, align engine versions and delete the stale log to force a clean fallback.
Prevention
- Create and serve databases with binaries built from the same source revision.
- After upgrading tursodb, expect old-version logs to fall back to replace-base; plan a client re-sync.
When it happens
Trigger: A .db-log written by a different engine build than the running sync server binary — e.g. the database was created by a newer turso checkout while cli/sync_server.rs runs from an older one.
Common situations: Mixed-version tooling on one machine after a pull and partial rebuild; upgrading tursodb while keeping old databases and logs around.
Related errors
- truncated MVCC logical log header: len={} header_size={}
- invalid MVCC logical log magic
- invalid MVCC logical log header flags
- MVCC logical pull revision is from the future: client_offset
- MVCC logical pull offset is not a transaction boundary: {off
AI-assisted analysis of tursodatabase/turso@bad083fafb (2026-08-16).
Data as JSON: /api/errors/44b04ca2fdd9c99c.
Report an issue: GitHub.