{"record":{"id":"af998367bdb2f3f4","repo":"tursodatabase/turso","slug":"mvcc-logical-log-extension-record-count-without-ex","errorCode":null,"errorMessage":"MVCC logical log extension record count without extension block at offset {offset}","messagePattern":"MVCC logical log extension record count without extension block at offset (.+?)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"cli/sync_server.rs","lineNumber":1063,"sourceCode":"        MVCC_TX_HEADER_SIZE\n    };\n    if log.len() - offset < header_size + MVCC_TX_TRAILER_SIZE {\n        return Ok(None);\n    }\n    let payload_size = usize::try_from(read_u64_le(log, offset + 4)?)\n        .map_err(|_| anyhow!(\"MVCC logical log payload size overflows usize\"))?;\n    let extension_size = if has_extension_header {\n        let extension_size = usize::try_from(read_u64_le(log, offset + 24)?)\n            .map_err(|_| anyhow!(\"MVCC logical log extension size overflows usize\"))?;\n        let extension_record_count = read_u32_le(log, offset + 32)?;\n        let frame_flags = read_u32_le(log, offset + 36)?;\n        if frame_flags & !MVCC_TX_FRAME_FLAG_HAS_EXTENSION_BLOCK != 0 {\n            return Err(anyhow!(\n                \"unsupported MVCC logical log frame flags at offset {offset}: {frame_flags:#x}\"\n            ));\n        }\n        if extension_size == 0 && extension_record_count != 0 {\n            return Err(anyhow!(\n                \"MVCC logical log extension record count without extension block at offset {offset}\"\n            ));\n        }\n        if extension_size > 0 && frame_flags & MVCC_TX_FRAME_FLAG_HAS_EXTENSION_BLOCK == 0 {\n            return Err(anyhow!(\n                \"MVCC logical log extension block missing flag at offset {offset}\"\n            ));\n        }\n        extension_size\n    } else {\n        0\n    };\n    let trailer_start = offset\n        .checked_add(header_size)\n        .and_then(|value| value.checked_add(payload_size))\n        .and_then(|value| value.checked_add(extension_size))\n        .ok_or_else(|| anyhow!(\"MVCC logical log frame offset overflow\"))?;\n    let frame_end = trailer_start","sourceCodeStart":1045,"sourceCodeEnd":1081,"githubUrl":"https://github.com/tursodatabase/turso/blob/bad083fafbefdeae9a42ec19bdaaad8918dcf411/cli/sync_server.rs#L1045-L1081","documentation":"An extension-header frame with extension_size == 0 (u64 at +24) must also have extension_record_count == 0 (u32 at +32). The two fields describe the same optional extension block; when they disagree the frame header is internally inconsistent, so the validator stops the scan rather than trust a trailer offset computed from contradictory metadata.","triggerScenarios":"Scanning an extension frame where extension_size is 0 but extension_record_count is nonzero — corruption of those bytes, or a hand-rolled log producer (test tool, migration script) that writes a record count without emitting an extension block.","commonSituations":"Test and tooling code that builds extension frames field-by-field; partial frame writes after a crash; fuzzing the extension-header path with mismatched fields.","solutions":["Re-pull or re-bootstrap the log; size/count disagreement is unrecoverable corruption.","If you write a log producer, zero extension_record_count whenever extension_size is 0.","Check whether a partial write crashed mid-frame on the writer side (producer logs, fsync failures)."],"exampleFix":"// before\nlet snapshot = scan_mvcc_log(&log)?; // errors: extension record count without extension block\n\n// after (log producer): keep the two extension fields consistent when building frames\nif extension_bytes.is_empty() {\n    extension_record_count = 0;\n}\nwrite_frame_header(&mut out, payload, extension_bytes, extension_record_count, flags);","handlingStrategy":"try-catch","validationCode":"fn extension_fields_consistent(log: &[u8], offset: usize) -> bool {\n    let size = log.get(offset + 24..offset + 32)\n        .map(|b| u64::from_le_bytes(b.try_into().unwrap()));\n    let count = log.get(offset + 32..offset + 36)\n        .map(|b| u32::from_le_bytes(b.try_into().unwrap()));\n    match (size, count) {\n        (Some(0), Some(c)) => c == 0,\n        (Some(_), Some(_)) => true,\n        _ => false,\n    }\n}","typeGuard":"fn extension_count_matches_size(log: &[u8], offset: usize) -> bool {\n    let size = log.get(offset + 24..offset + 32)\n        .map(|b| u64::from_le_bytes(b.try_into().unwrap()));\n    let count = log.get(offset + 32..offset + 36)\n        .map(|b| u32::from_le_bytes(b.try_into().unwrap()));\n    !(size == Some(0) && count.is_some_and(|c| c != 0))\n}","tryCatchPattern":"match scan_mvcc_log(&log) {\n    Ok(snapshot) => { /* serve deltas */ }\n    Err(err) if err.to_string().contains(\"extension record count without extension block\") => {\n        // internally inconsistent frame header: re-bootstrap the log\n    }\n    Err(err) => return Err(err),\n}","preventionTips":["When building frames programmatically, zero the record count when there is no extension block.","Re-pull on consistency failures; they never resolve by retrying.","Fuzz extension-frame parsing to keep producers and the validator in agreement."],"tags":["mvcc","turso","sync","file-format","consistency"],"backgroundTag":"file-format-validation-failed","analyzedSha":"bad083fafbefdeae9a42ec19bdaaad8918dcf411","analyzedAt":"2026-08-16T23:12:11.798Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}