tursodatabase/turso · error · anyhow::Error
MVCC logical log extension record count without extension bl
Error message
MVCC logical log extension record count without extension block at offset {offset} What it means
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.
Source
Thrown at cli/sync_server.rs:1063
MVCC_TX_HEADER_SIZE
};
if log.len() - offset < header_size + MVCC_TX_TRAILER_SIZE {
return Ok(None);
}
let payload_size = usize::try_from(read_u64_le(log, offset + 4)?)
.map_err(|_| anyhow!("MVCC logical log payload size overflows usize"))?;
let extension_size = if has_extension_header {
let extension_size = usize::try_from(read_u64_le(log, offset + 24)?)
.map_err(|_| anyhow!("MVCC logical log extension size overflows usize"))?;
let extension_record_count = read_u32_le(log, offset + 32)?;
let frame_flags = read_u32_le(log, offset + 36)?;
if frame_flags & !MVCC_TX_FRAME_FLAG_HAS_EXTENSION_BLOCK != 0 {
return Err(anyhow!(
"unsupported MVCC logical log frame flags at offset {offset}: {frame_flags:#x}"
));
}
if extension_size == 0 && extension_record_count != 0 {
return Err(anyhow!(
"MVCC logical log extension record count without extension block at offset {offset}"
));
}
if extension_size > 0 && frame_flags & MVCC_TX_FRAME_FLAG_HAS_EXTENSION_BLOCK == 0 {
return Err(anyhow!(
"MVCC logical log extension block missing flag at offset {offset}"
));
}
extension_size
} else {
0
};
let trailer_start = offset
.checked_add(header_size)
.and_then(|value| value.checked_add(payload_size))
.and_then(|value| value.checked_add(extension_size))
.ok_or_else(|| anyhow!("MVCC logical log frame offset overflow"))?;
let frame_end = trailer_startView on GitHub (pinned to bad083fafb)
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).
Example fix
// before
let snapshot = scan_mvcc_log(&log)?; // errors: extension record count without extension block
// after (log producer): keep the two extension fields consistent when building frames
if extension_bytes.is_empty() {
extension_record_count = 0;
}
write_frame_header(&mut out, payload, extension_bytes, extension_record_count, flags); Defensive patterns
Strategy: try-catch
Validate before calling
fn extension_fields_consistent(log: &[u8], offset: usize) -> bool {
let size = log.get(offset + 24..offset + 32)
.map(|b| u64::from_le_bytes(b.try_into().unwrap()));
let count = log.get(offset + 32..offset + 36)
.map(|b| u32::from_le_bytes(b.try_into().unwrap()));
match (size, count) {
(Some(0), Some(c)) => c == 0,
(Some(_), Some(_)) => true,
_ => false,
}
} Type guard
fn extension_count_matches_size(log: &[u8], offset: usize) -> bool {
let size = log.get(offset + 24..offset + 32)
.map(|b| u64::from_le_bytes(b.try_into().unwrap()));
let count = log.get(offset + 32..offset + 36)
.map(|b| u32::from_le_bytes(b.try_into().unwrap()));
!(size == Some(0) && count.is_some_and(|c| c != 0))
} Try / catch
match scan_mvcc_log(&log) {
Ok(snapshot) => { /* serve deltas */ }
Err(err) if err.to_string().contains("extension record count without extension block") => {
// internally inconsistent frame header: re-bootstrap the log
}
Err(err) => return Err(err),
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- MVCC logical log extension block missing flag at offset {off
- invalid MVCC logical log header length: {header_len}
- MVCC logical log header reserved bytes must be zero
- MVCC logical log header checksum mismatch
- invalid MVCC logical log frame magic at offset {offset}: {fr
AI-assisted analysis of tursodatabase/turso@bad083fafb (2026-08-16).
Data as JSON: /api/errors/af998367bdb2f3f4.
Report an issue: GitHub.