tursodatabase/turso · error · anyhow::Error

MVCC logical log extension block missing flag at offset {off

Error message

MVCC logical log extension block missing flag at offset {offset}

What it means

The inverse consistency rule: extension_size > 0 requires MVCC_TX_FRAME_FLAG_HAS_EXTENSION_BLOCK set in the flags word at offset +36. A non-empty extension block that the flags word does not announce is self-contradictory, and the reader cannot trust the trailer offset derived from the extension size. The scan rejects the frame outright.

Source

Thrown at cli/sync_server.rs:1068

    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
        .checked_add(header_size)
        .and_then(|value| value.checked_add(payload_size))
        .and_then(|value| value.checked_add(extension_size))
        .ok_or_else(|| anyhow!("MVCC logical log frame offset overflow"))?;
    let frame_end = trailer_start
        .checked_add(MVCC_TX_TRAILER_SIZE)
        .ok_or_else(|| anyhow!("MVCC logical log frame end overflow"))?;
    if frame_end > log.len() {
        return Ok(None);
    }

View on GitHub (pinned to bad083fafb)

Solutions

  1. Re-pull the log; the frame header contradicts itself and cannot be parsed safely.
  2. If producing frames, always set MVCC_TX_FRAME_FLAG_HAS_EXTENSION_BLOCK when extension_size > 0.
  3. Audit tools that rewrite frames to keep flags and sizes in sync.

Example fix

// before
write_frame_header(&mut out, payload, extension_bytes, record_count, 0); // flag missing

// after
let flags = if extension_bytes.is_empty() { 0 } else { MVCC_TX_FRAME_FLAG_HAS_EXTENSION_BLOCK };
write_frame_header(&mut out, payload, extension_bytes, record_count, flags);
Defensive patterns

Strategy: try-catch

Validate before calling

fn extension_flag_matches_size(log: &[u8], offset: usize) -> bool {
    let size = log.get(offset + 24..offset + 32)
        .map(|b| u64::from_le_bytes(b.try_into().unwrap()))
        .unwrap_or(0);
    let flags = log.get(offset + 36..offset + 40)
        .map(|b| u32::from_le_bytes(b.try_into().unwrap()))
        .unwrap_or(0);
    (size > 0) == (flags & MVCC_TX_FRAME_FLAG_HAS_EXTENSION_BLOCK != 0)
}

Type guard

fn extension_flag_matches_size(log: &[u8], offset: usize) -> bool {
    let size = log.get(offset + 24..offset + 32)
        .map(|b| u64::from_le_bytes(b.try_into().unwrap()))
        .unwrap_or(0);
    let flags = log.get(offset + 36..offset + 40)
        .map(|b| u32::from_le_bytes(b.try_into().unwrap()))
        .unwrap_or(0);
    (size > 0) == (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().contains("extension block missing flag") => {
        // flags contradict the size field: re-pull the log
    }
    Err(err) => return Err(err),
}

Prevention

When it happens

Trigger: Scanning an extension frame with a nonzero extension size whose flags word lacks MVCC_TX_FRAME_FLAG_HAS_EXTENSION_BLOCK — a producer that writes the block but forgets to set the flag, or corruption that cleared the flag bit while leaving the size intact.

Common situations: Hand-written frame builders in tests or tooling; tools that rewrite or splice frames without updating flags; bit flips confined to the flags word.

Related errors


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