{"record":{"id":"44b04ca2fdd9c99c","repo":"tursodatabase/turso","slug":"unsupported-mvcc-logical-log-version","errorCode":null,"errorMessage":"unsupported MVCC logical log version {}","messagePattern":"unsupported MVCC logical log version (.+?)","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"cli/sync_server.rs","lineNumber":988,"sourceCode":"    }\n\n    Ok(MvccLogSnapshot {\n        end_offset: offset as u64,\n        crc_by_offset,\n    })\n}\n\nfn is_nonportable_mvcc_log_error(err: &anyhow::Error) -> bool {\n    let message = err.to_string();\n    message.starts_with(\"unsupported MVCC logical log version \")\n}\n\nfn validate_mvcc_log_header(log: &[u8]) -> Result<()> {\n    if read_u32_le(log, 0)? != MVCC_LOG_MAGIC {\n        return Err(anyhow!(\"invalid MVCC logical log magic\"));\n    }\n    if log[4] != MVCC_LOG_VERSION {\n        return Err(anyhow!(\"unsupported MVCC logical log version {}\", log[4]));\n    }\n    if log[5] & 0b1111_1110 != 0 {\n        return Err(anyhow!(\"invalid MVCC logical log header flags\"));\n    }\n    let header_len = u16::from_le_bytes([log[6], log[7]]) as usize;\n    if header_len != MVCC_LOG_HEADER_SIZE {\n        return Err(anyhow!(\n            \"invalid MVCC logical log header length: {header_len}\"\n        ));\n    }\n    if log[MVCC_LOG_HEADER_RESERVED_START..MVCC_LOG_HEADER_CRC_START]\n        .iter()\n        .any(|byte| *byte != 0)\n    {\n        return Err(anyhow!(\n            \"MVCC logical log header reserved bytes must be zero\"\n        ));\n    }","sourceCodeStart":970,"sourceCodeEnd":1006,"githubUrl":"https://github.com/tursodatabase/turso/blob/bad083fafbefdeae9a42ec19bdaaad8918dcf411/cli/sync_server.rs#L970-L1006","documentation":"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.","triggerScenarios":"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.","commonSituations":"Mixed-version tooling on one machine after a pull and partial rebuild; upgrading tursodb while keeping old databases and logs around.","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."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"fn log_version(path: &std::path::Path) -> Option<u8> {\n    let mut buf = [0u8; 5];\n    let mut f = std::fs::File::open(path).ok()?;\n    f.read_exact(&mut buf).ok()?;\n    (u32::from_le_bytes(buf[..4].try_into().unwrap()) == 0x4C4D4C32).then_some(buf[4])\n}\n// expect Some(3) for this server; other versions trigger replace-base fallback","typeGuard":null,"tryCatchPattern":"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.","preventionTips":["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."],"tags":["mvcc","logical-log","version-skew","file-format","compatibility"],"backgroundTag":"format-version-mismatch","analyzedSha":"bad083fafbefdeae9a42ec19bdaaad8918dcf411","analyzedAt":"2026-08-16T23:12:11.798Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}