tursodatabase/turso · error · anyhow::Error
MVCC logical log extension block missing flag at offset {off
Error message
MVCC logical log extension block missing flag at offset {offset} What it means
The inverse consistency rule: extension_size > 0 requires MVCC_TX_FRAME_FLAG_HAS_EXTENSION_BLOCK set in the flags word at offset +36. A non-empty extension block that the flags word does not announce is self-contradictory, and the reader cannot trust the trailer offset derived from the extension size. The scan rejects the frame outright.
Source
Thrown at cli/sync_server.rs:1068
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_start
.checked_add(MVCC_TX_TRAILER_SIZE)
.ok_or_else(|| anyhow!("MVCC logical log frame end overflow"))?;
if frame_end > log.len() {
return Ok(None);
}View on GitHub (pinned to bad083fafb)
Solutions
- Re-pull the log; the frame header contradicts itself and cannot be parsed safely.
- If producing frames, always set MVCC_TX_FRAME_FLAG_HAS_EXTENSION_BLOCK when extension_size > 0.
- Audit tools that rewrite frames to keep flags and sizes in sync.
Example fix
// before
write_frame_header(&mut out, payload, extension_bytes, record_count, 0); // flag missing
// after
let flags = if extension_bytes.is_empty() { 0 } else { MVCC_TX_FRAME_FLAG_HAS_EXTENSION_BLOCK };
write_frame_header(&mut out, payload, extension_bytes, record_count, flags); Defensive patterns
Strategy: try-catch
Validate before calling
fn extension_flag_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()))
.unwrap_or(0);
let flags = log.get(offset + 36..offset + 40)
.map(|b| u32::from_le_bytes(b.try_into().unwrap()))
.unwrap_or(0);
(size > 0) == (flags & MVCC_TX_FRAME_FLAG_HAS_EXTENSION_BLOCK != 0)
} Type guard
fn extension_flag_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()))
.unwrap_or(0);
let flags = log.get(offset + 36..offset + 40)
.map(|b| u32::from_le_bytes(b.try_into().unwrap()))
.unwrap_or(0);
(size > 0) == (flags & MVCC_TX_FRAME_FLAG_HAS_EXTENSION_BLOCK != 0)
} Try / catch
match scan_mvcc_log(&log) {
Ok(snapshot) => { /* serve deltas */ }
Err(err) if err.to_string().contains("extension block missing flag") => {
// flags contradict the size field: re-pull the log
}
Err(err) => return Err(err),
} Prevention
- Derive the flag from the block size in producers: set it iff extension_size > 0.
- Never rewrite frame bytes without recomputing both flags and sizes.
- Keep test frame builders aligned with the validator rules.
When it happens
Trigger: Scanning an extension frame with a nonzero extension size whose flags word lacks MVCC_TX_FRAME_FLAG_HAS_EXTENSION_BLOCK — a producer that writes the block but forgets to set the flag, or corruption that cleared the flag bit while leaving the size intact.
Common situations: Hand-written frame builders in tests or tooling; tools that rewrite or splice frames without updating flags; bit flips confined to the flags word.
Related errors
- MVCC logical log extension record count without extension bl
- 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/bb1ba6d765b7d818.
Report an issue: GitHub.