{"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":"warning","filePath":"cli/sync_server.rs","lineNumber":1103,"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":1085,"sourceCodeEnd":1121,"githubUrl":"https://github.com/tursodatabase/turso/blob/492c4a71cd7c2649e7df83da1471b74f4b1c7aa9/cli/sync_server.rs#L1085-L1121","documentation":"TursoSyncServer serves incremental MVCC sync from an append-only logical log whose 56-byte header encodes a format version in byte 4 (after the 0x4C4D4C32 magic); this build only accepts MVCC_LOG_VERSION = 3. validate_mvcc_log_header throws when that byte differs, meaning the log was written by an older or newer tursodb release. It is the one error is_nonportable_mvcc_log_error recognizes, so /pull-updates degrades to a replace-base (full pages) fallback instead of failing the request.","triggerScenarios":"A POST /pull-updates logical pull where the MVCC log file exists and is at least 56 bytes, and scan_mvcc_log -> validate_mvcc_log_header reads log[4] with any value other than 3 - e.g. a version-2 log from an older build or version 4+ from a newer build.","commonSituations":"Upgrading or downgrading the tursodb sync-server binary while keeping the old MVCC logical log on disk; copying a log file between nodes running different releases; replaying a captured log from CI against a newer parser.","solutions":["Run the sync server with the same tursodb release that wrote the MVCC log (byte 4 must equal MVCC_LOG_VERSION = 3)","If version skew is intentional, archive or delete the MVCC logical log so the server starts fresh; clients resynchronize through the replace-base fallback","When changing the log format in a fork, bump MVCC_LOG_VERSION and extend is_nonportable_mvcc_log_error so older readers fall back instead of erroring"],"exampleFix":"// before\nlet snapshot = scan_mvcc_log(&log)?;\n\n// after: version skew falls back to replace-base pages instead of failing\nlet snapshot = match scan_mvcc_log(&log) {\n    Ok(snapshot) => snapshot,\n    Err(err) if is_nonportable_mvcc_log_error(&err) => {\n        info!(\"MVCC log not portable; serving replace-base pages: {err}\");\n        return self.handle_logical_fallback(\n            req,\n            fallback_revision,\n            &legacy_current_revision,\n            db_size,\n        );\n    }\n    Err(err) => return Err(err),\n};","handlingStrategy":"fallback","validationCode":"// Before serving logical pulls, check the version byte:\nfn mvcc_log_version_is_supported(log: &[u8]) -> bool {\n    log.len() >= 5 && log[4] == MVCC_LOG_VERSION // 3 in this build\n}","typeGuard":null,"tryCatchPattern":"match scan_mvcc_log(&log) {\n    Ok(snapshot) => { /* serve logical deltas */ }\n    Err(err) if is_nonportable_mvcc_log_error(&err) => {\n        // version skew: degrade to replace-base pages\n        self.handle_logical_fallback(req, fallback_revision, &legacy_current_revision, db_size)\n    }\n    Err(err) => Err(err),\n}","preventionTips":["Keep the sync-server binary and the process that writes the MVCC log on the same tursodb release","Back up or delete the MVCC logical log before upgrading/downgrading the binary","Never hand-copy logical logs between nodes running different versions"],"tags":["mvcc","sync-server","version-mismatch","file-format"],"backgroundTag":"file-format-version-mismatch","analyzedSha":"492c4a71cd7c2649e7df83da1471b74f4b1c7aa9","analyzedAt":"2026-08-20T07:02:18.389Z","contentChangedAt":"2026-08-20T07:02:18.389Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}