tursodatabase/turso · error · anyhow::Error

serial types and do not store the same number of bytes

Error message

serial types {} and {} do not store the same number of bytes

What it means

When the sqltest fixture generator patches the first row's serial type in a raw database page, it requires that the old and new serial types encode the same payload size, because the patch is an in-place byte rewrite that cannot move data. Mismatched sizes would corrupt the record layout, so the generator rejects the patch up front.

Solutions

  1. Choose a `new_serial_type` with the same payload length as the original (same-size types: 0/8/9/12/13 are 0 bytes; 1/2/3/4/5/6 differ; 7 is 8 bytes).
  2. Verify with `sqlite_serial_type_payload_len` for both types before patching.
  3. If a different size is truly needed, write a full record rewrite instead of an in-place serial-type patch.

Example fix

// before
patch.expected_serial_type = 1; // 1 byte
patch.new_serial_type = 4;      // 4 bytes -> rejected
// after
patch.expected_serial_type = 1; // 1 byte
patch.new_serial_type = 2;      // also 1 byte... use matching sizes, e.g. 1 -> 2
Defensive patterns

Strategy: validation

Validate before calling

use sqlite_serial_type_payload_len;
assert_eq!(
    sqlite_serial_type_payload_len(u64::from(patch.expected_serial_type)),
    sqlite_serial_type_payload_len(u64::from(patch.new_serial_type)),
    "patch must replace a serial type with same payload size"
);

Prevention

When it happens

Trigger: Calling `patch_first_row_serial_type` with a `FirstRowSerialTypePatch` whose `expected_serial_type` and `new_serial_type` have different payload lengths (e.g. replacing serial type 1 / int8 with serial type 4 / int32), via `generate_not_null_violation_fixture`, `generate_gencol_not_null_violation_fixture`, or `generate_strict_fixture`.

Common situations: Editing fixture definitions and choosing a replacement serial type of a different width, or hand-crafting a corrupt-fixture patch for a new test case.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of tursodatabase/turso@8d4a589f8d (2026-09-20). Data as JSON: /api/errors/bd2b9409bd22d5d4. Report an issue: GitHub.

Appendix: source

Thrown at testing/sqltest/src/generator/mod.rs:1029

    let table_root_page = get_root_page(&conn, "t", relative_path).await?;

    drop(conn);
    drop(db);
    patch_first_row_serial_type(db_path, page_size, table_root_page, patch)?;
    remove_db_sidecars(db_path)?;
    Ok(())
}

/// Replace the serial type of one column in the first row of a table page. Both
/// serial types must store the same number of bytes, so that the rest of the
/// record keeps its place.
fn patch_first_row_serial_type(
    db_path: &Path,
    page_size: usize,
    root_page: usize,
    patch: FirstRowSerialTypePatch,
) -> Result<()> {
    anyhow::ensure!(
        sqlite_serial_type_payload_len(u64::from(patch.expected_serial_type))
            == sqlite_serial_type_payload_len(u64::from(patch.new_serial_type)),
        "serial types {} and {} do not store the same number of bytes",
        patch.expected_serial_type,
        patch.new_serial_type
    );

    let mut bytes = std::fs::read(db_path)
        .with_context(|| format!("failed to read fixture '{}'", db_path.display()))?;
    let page_start = (root_page - 1) * page_size;
    anyhow::ensure!(
        bytes.len() > page_start + 8,
        "fixture too small to patch page {root_page}"
    );

    let cell_count = u16::from_be_bytes([bytes[page_start + 3], bytes[page_start + 4]]) as usize;
    anyhow::ensure!(
        cell_count >= 1,

View on GitHub (pinned to 8d4a589f8d)