tursodatabase/turso · error · anyhow::Error

buffer too short for u64 at offset {offset}

Error message

buffer too short for u64 at offset {offset}

What it means

The u64 counterpart of read_u32_le: 8 little-endian bytes at offset..offset+8 must lie inside the buffer or this error returns. It backs every multi-byte field read in the MVCC log scanner (payload size, extension size, salt). Reaching it via scan_mvcc_log means a length invariant broke; reaching it in new code means the caller skipped a bounds check.

Source

Thrown at cli/sync_server.rs:1113

    if end_magic != MVCC_TX_END_MAGIC {
        return Err(anyhow!(
            "invalid MVCC logical log frame end magic at offset {offset}"
        ));
    }
    Ok(Some((frame_end, stored_crc)))
}

fn read_u32_le(buf: &[u8], offset: usize) -> Result<u32> {
    let bytes = buf
        .get(offset..offset + 4)
        .ok_or_else(|| anyhow!("buffer too short for u32 at offset {offset}"))?;
    Ok(u32::from_le_bytes(bytes.try_into().unwrap()))
}

fn read_u64_le(buf: &[u8], offset: usize) -> Result<u64> {
    let bytes = buf
        .get(offset..offset + 8)
        .ok_or_else(|| anyhow!("buffer too short for u64 at offset {offset}"))?;
    Ok(u64::from_le_bytes(bytes.try_into().unwrap()))
}

fn current_db_size_pages(conn: &Connection, max_frame: u64) -> Result<u64> {
    if max_frame > 0 {
        let frame_size = WAL_FRAME_HEADER_SIZE + PAGE_SIZE;
        let mut last_frame = vec![0u8; frame_size];
        let last_info = conn.wal_get_frame(max_frame, &mut last_frame)?;
        Ok(last_info.db_size as u64)
    } else {
        Ok(0)
    }
}

fn current_snapshot_db_size_pages(conn: &Connection, max_frame: u64) -> Result<u64> {
    if max_frame > 0 {
        return current_db_size_pages(conn, max_frame);
    }

View on GitHub (pinned to bad083fafb)

Solutions

  1. Check buf.len() >= offset + 8 before calling read_u64_le.
  2. Use offset.checked_add(8) to also rule out offset overflow.
  3. If it fires inside the scanner proper, treat it as a regression and report the log plus offsets.

Example fix

// before
let size = read_u64_le(buf, offset)?; // may error: buffer too short for u64

// after
anyhow::ensure!(
    offset.checked_add(8).is_some_and(|end| end <= buf.len()),
    "no room for u64 at offset {offset}"
);
let size = read_u64_le(buf, offset)?;
Defensive patterns

Strategy: validation

Validate before calling

fn has_u64_at(buf: &[u8], offset: usize) -> bool {
    offset.checked_add(8).is_some_and(|end| end <= buf.len())
}
// before reading:
anyhow::ensure!(has_u64_at(buf, offset), "no room for u64 at offset {offset}");
let value = read_u64_le(buf, offset)?;

Type guard

fn has_u64_at(buf: &[u8], offset: usize) -> bool {
    offset.checked_add(8).is_some_and(|end| end <= buf.len())
}

Try / catch

match read_u64_le(buf, offset) {
    Ok(value) => { /* use value */ }
    Err(err) if err.to_string().contains("buffer too short for u64") => {
        // caller bug: add the missing bounds check at this call site
    }
    Err(err) => return Err(err),
}

Prevention

When it happens

Trigger: read_u64_le(buf, offset) with fewer than 8 bytes remaining at offset — same shape as the u32 helper: unchecked callers, refactors that removed size pre-checks, or off-by-one offset arithmetic.

Common situations: New code reading 8-byte length fields from short buffers; fuzzed truncated inputs; refactors that changed frame-layout constants without updating bounds checks.

Related errors


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