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

  1. Upgrade the sync server to the tursodb version that wrote the log
  2. Regenerate the MVCC log if the skew is one-way and clients can resync
  3. 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

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


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

View on GitHub (pinned to 492c4a71cd)