tursodatabase/turso · error · anyhow::Error
invalid MVCC logical log frame magic at offset {offset}: {fr
Error message
invalid MVCC logical log frame magic at offset {offset}: {frame_magic:#x} What it means
After the 56-byte header, scan_mvcc_log walks frames; each must begin with magic 0x5854564D (MVCC_TX_FRAME_MAGIC) or 0x5845564D (MVCC_TX_EXT_FRAME_MAGIC, the extension-header variant). This error fires at the exact byte offset where neither magic was found, meaning the scan reached bytes that are not a frame start. A genuinely truncated final frame returns Ok(None) instead, so this error implies real corruption or misalignment, not a clean short read.
Source
Thrown at cli/sync_server.rs:1038
log[MVCC_LOG_HEADER_SALT_START..MVCC_LOG_HEADER_SALT_END]
.try_into()
.expect("fixed-size salt slice"),
);
Ok(crc32c::crc32c(&salt.to_le_bytes()))
}
fn read_mvcc_frame_boundary(
log: &[u8],
offset: usize,
running_crc: u32,
) -> Result<Option<(usize, u32)>> {
if log.len() - offset < MVCC_TX_HEADER_SIZE + MVCC_TX_TRAILER_SIZE {
return Ok(None);
}
let frame_magic = read_u32_le(log, offset)?;
let has_extension_header = frame_magic == MVCC_TX_EXT_FRAME_MAGIC;
if frame_magic != MVCC_TX_FRAME_MAGIC && !has_extension_header {
return Err(anyhow!(
"invalid MVCC logical log frame magic at offset {offset}: {frame_magic:#x}"
));
}
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)?;View on GitHub (pinned to bad083fafb)
Solutions
- Re-pull the full logical log from a consistent snapshot; a bad frame magic means frame boundaries are lost and incremental parsing cannot continue.
- Hexdump around the reported offset and compare with the producer's copy to see whether bytes were inserted or dropped.
- Ensure exactly one writer appends to the log file; coordinate or serialize writers.
- Confirm the log was never concatenated with another file or padded.
Example fix
// before
let snapshot = scan_mvcc_log(&log)?; // errors: invalid ... frame magic at offset N
// after: a lost boundary is unrecoverable — fall back to full bootstrap
let snapshot = match scan_mvcc_log(&log) {
Ok(snapshot) => snapshot,
Err(err) if err.to_string().contains("frame magic") => full_bootstrap()?,
Err(err) => return Err(err),
}; Defensive patterns
Strategy: try-catch
Type guard
fn frame_magic_known(log: &[u8], offset: usize) -> bool {
log.get(offset..offset + 4)
.map(|b| u32::from_le_bytes(b.try_into().unwrap()))
.is_some_and(|m| m == MVCC_TX_FRAME_MAGIC || m == MVCC_TX_EXT_FRAME_MAGIC)
} Try / catch
match scan_mvcc_log(&log) {
Ok(snapshot) => { /* serve deltas */ }
Err(err) if err.to_string().contains("frame magic") => {
// frame boundary lost: fall back to a full bootstrap, not a delta pull
}
Err(err) => return Err(err),
} Prevention
- Allow exactly one writer to append to a logical log file.
- Never concatenate logs from different generations or splice foreign bytes in.
- Copy logs atomically (temp file + rename) rather than in-place while appended.
- Compare against the producer's copy when the reported offset looks valid.
When it happens
Trigger: scan_mvcc_log reaches an offset (just past the header or a previous frame end) whose leading u32 is neither 0x5854564D nor 0x5845564D: bytes inserted or dropped mid-log, a log concatenated with foreign data, two uncoordinated writers appending to the same file, or storage returning garbage after a crash.
Common situations: Concurrent appends to one logical log without coordination; a log truncated and re-appended from the wrong offset; concatenating logs from different generations; partially-copied files where the copy resumed at the wrong byte; fuzzed log inputs.
Related errors
- invalid MVCC logical log frame end magic at offset {offset}
- MVCC logical log frame checksum mismatch at offset {offset}
- invalid MVCC logical log header length: {header_len}
- MVCC logical log header reserved bytes must be zero
- MVCC logical log header checksum mismatch
AI-assisted analysis of tursodatabase/turso@bad083fafb (2026-08-16).
Data as JSON: /api/errors/92c41f0424c1650e.
Report an issue: GitHub.