tursodatabase/turso · error · anyhow::Error
unsupported MVCC logical log frame flags at offset
Error message
unsupported MVCC logical log frame flags at offset {offset}: {frame_flags:#x} What it means
Extended frames store a u32 flags word at offset+36; this build only defines MVCC_TX_FRAME_FLAG_HAS_EXTENSION_BLOCK and rejects any other bits. Unknown flag bits mean the frame was written by a format revision newer than the reader, or the flags word is corrupt.
Solutions
- Upgrade the sync server to the tursodb version that wrote the log
- Regenerate the MVCC log if the skew is one-way and clients can resync
- If maintaining the format, add flags only together with a MVCC_LOG_VERSION bump so old readers reject cleanly
Defensive patterns
Strategy: try-catch
Try / catch
let snapshot = match scan_mvcc_log(&log) {
Ok(s) => s,
Err(err) if err.to_string().starts_with("unsupported MVCC logical log frame flags") => {
// likely written by a newer format revision: regenerate log or upgrade reader
upgrade_or_regenerate()?
}
Err(err) => return Err(err),
}; Prevention
- Keep writer and reader versions in lockstep when frames carry flags
- Bump MVCC_LOG_VERSION whenever new frame flags are introduced
- Log the offending flags value so skew can be diagnosed from server logs
When it happens
Trigger: read_mvcc_frame_boundary evaluates frame_flags & !MVCC_TX_FRAME_FLAG_HAS_EXTENSION_BLOCK != 0 on an extension frame - e.g. a newer tursodb that defined additional frame flags wrote the log.
Common situations: Version skew: log written by a newer build, served by an older one; corruption of the flags word; forks that add flags without bumping MVCC_LOG_VERSION.
Related errors
- unsupported MVCC logical log version
- buffer too short for u32 at offset
- buffer too short for u64 at offset
- invalid MVCC logical log frame end magic at offset
- invalid MVCC logical log frame magic at offset
AI-assisted analysis of tursodatabase/turso@492c4a71cd (2026-08-20).
Data as JSON: /api/errors/2c6ff5fa4469182c.
Report an issue: GitHub.
Appendix: source
Thrown at cli/sync_server.rs:1173
));
}
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 492c4a71cd)