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 = offset

View on GitHub (pinned to bad083fafb)

Solutions

  1. Upgrade the sync server (reader) to the engine version that wrote the log — new flag bits are a newer frame format.
  2. Or regenerate the log with the current engine so it uses only known flags.
  3. 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

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


AI-assisted analysis of tursodatabase/turso@bad083fafb (2026-08-16). Data as JSON: /api/errors/2c6ff5fa4469182c. Report an issue: GitHub.