tursodatabase/turso · error · anyhow::Error
unsupported MVCC logical log frame flags at offset {offset}:
Error message
unsupported MVCC logical log frame flags at offset {offset}: {frame_flags:#x} What it means
Extension frames carry a u32 flags word at offset +36; this build defines only MVCC_TX_FRAME_FLAG_HAS_EXTENSION_BLOCK. Any other bit set means the frame uses a format this binary does not know how to skip, so the scan rejects it instead of guessing the layout. It is the forward-compatibility guard: newer engines that define new flag bits will trip it on older readers, mirroring the version check in the header.
Source
Thrown at cli/sync_server.rs:1058
));
}
let header_size = if has_extension_header {
MVCC_TX_EXT_HEADER_SIZE
} else {
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 = offsetView on GitHub (pinned to bad083fafb)
Solutions
- Upgrade the sync server (reader) to the engine version that wrote the log — new flag bits are a newer frame format.
- Or regenerate the log with the current engine so it uses only known flags.
- Keep both ends of sync on the same Turso release; treat this error like the nonportable-version error (is_nonportable_mvcc_log_error) rather than corruption.
Example fix
// before
let snapshot = scan_mvcc_log(&log)?; // errors: unsupported ... frame flags at offset N
// after: classify as a version-compat failure, not corruption
let snapshot = match scan_mvcc_log(&log) {
Ok(snapshot) => snapshot,
Err(err) if err.to_string().starts_with("unsupported MVCC logical log frame flags") => {
return Err(err.context("sync server older than log writer; upgrade both ends"));
}
Err(err) => return Err(err),
}; Defensive patterns
Strategy: try-catch
Type guard
fn frame_flags_supported(log: &[u8], offset: usize) -> bool {
log.get(offset + 36..offset + 40).is_some_and(|b| {
let flags = u32::from_le_bytes(b.try_into().unwrap());
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().starts_with("unsupported MVCC logical log frame flags") => {
// version-compat failure like is_nonportable_mvcc_log_error:
// upgrade the reader; do not partially trust the log
}
Err(err) => return Err(err),
} Prevention
- Upgrade writer and reader engines together during rollouts.
- Treat unknown-flag errors as nonportable-format errors, not corruption.
- Add CI coverage that scans logs produced by the current engine with the shipped sync server.
When it happens
Trigger: Scanning an extension frame whose u32 flags at offset +36 contain bits outside MVCC_TX_FRAME_FLAG_HAS_EXTENSION_BLOCK — typically a log written by a newer engine revision that defined new flags, read by an older sync server; also corrupted flags bytes.
Common situations: Rolling upgrades where the writer node upgrades first and emits new flag bits while the sync server still runs the old release; logs produced by feature-branch builds scanned by a main-branch server; fuzzed flag words.
Related errors
- 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
- MVCC logical log extension record count without extension bl
AI-assisted analysis of tursodatabase/turso@bad083fafb (2026-08-16).
Data as JSON: /api/errors/2c6ff5fa4469182c.
Report an issue: GitHub.