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

  1. Rebuild/reinstall the binary that created the .db and the sync server from the same source revision.
  2. Delete the old-version .db-log: the server automatically falls back to replace-base pages and clients re-sync.
  3. 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

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


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