tursodatabase/turso · error · anyhow::Error

unexpected serial type

Error message

unexpected serial type {} for column {} of the fixture row, expected {}

What it means

The generator verifies that the serial-type byte at the target column's header offset equals `patch.expected_serial_type` before rewriting it. A mismatch means the stored value's type differs from what the patch assumed (e.g. the value isn't stored as INTEGER), so an in-place patch would produce an invalid record and the generator refuses.

Solutions

  1. Update `patch.expected_serial_type` to match the actual stored serial type in the fixture row.
  2. Adjust the fixture's INSERT so the first row stores the assumed type (e.g. a non-NULL integer).
  3. Re-derive the expected serial type from the schema/affinity before patching.

Example fix

// before
let patch = FirstRowSerialTypePatch { expected_serial_type: 1, .. }; // row stores NULL (0)
// after
// fixture: INSERT INTO t VALUES (1, ...) ensuring NOT NULL column stores integer
let patch = FirstRowSerialTypePatch { expected_serial_type: 1, new_serial_type: 0, .. };
Defensive patterns

Strategy: validation

Validate before calling

let actual = read_serial_type_at(&db_path, root_page, patch.column_index)?;
assert_eq!(actual, patch.expected_serial_type, "fixture row column {} stores serial type {}", patch.column_index, actual);

Prevention

When it happens

Trigger: `patch_first_row_serial_type` finds `bytes[serial_offset] != patch.expected_serial_type` — e.g. the column was created without NOT NULL, stores NULL (type 0), or the schema affinity changed the stored type.

Common situations: Fixture schema changed so the first row's value serializes differently, patching a generated column that stores a different type, or reusing a patch built for another fixture variant.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

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

    let (header_size, header_size_varint_len) = parse_sqlite_varint(&bytes, payload_start)?;
    let header_end = payload_start + header_size as usize;
    anyhow::ensure!(
        header_end <= bytes.len(),
        "record header out of bounds on page {root_page}"
    );

    let mut serial_offset = payload_start + header_size_varint_len;
    for _ in 0..patch.column_index {
        let (_, serial_len) = parse_sqlite_varint(&bytes, serial_offset)?;
        serial_offset += serial_len;
    }
    anyhow::ensure!(
        serial_offset < header_end,
        "record has no column {} on page {root_page}",
        patch.column_index
    );
    anyhow::ensure!(
        bytes[serial_offset] == patch.expected_serial_type,
        "unexpected serial type {} for column {} of the fixture row, expected {}",
        bytes[serial_offset],
        patch.column_index,
        patch.expected_serial_type
    );
    bytes[serial_offset] = patch.new_serial_type;

    std::fs::write(db_path, bytes)
        .with_context(|| format!("failed to write fixture '{}'", db_path.display()))?;
    Ok(())
}

async fn generate_non_unique_index_entry_fixture(db_path: &Path) -> Result<()> {
    clear_existing_db_and_sidecars(db_path)?;

    let db_path_str = db_path.to_string_lossy().to_string();
    let db = Builder::new_local(&db_path_str)

View on GitHub (pinned to 8d4a589f8d)