tursodatabase/turso · error · anyhow::Error
record header out of bounds on page
Error message
record header out of bounds on page {root_page} What it means
After locating the payload, the generator parses the record header size and checks that the whole header fits inside the page bytes. If `payload_start + header_size` exceeds the page length, the page bytes don't match the expected SQLite record format and patching would read garbage, so it aborts for that root page.
Solutions
- Verify page_size matches the actual database page size.
- Ensure the target row's record fits entirely on the leaf page (no overflow).
- Re-check that `root_page` points at the table's leaf page containing the first row.
Example fix
// before let page_size = 4096; // db actually uses 512 // after let page_size = db_page_size(&db_path)?;
Defensive patterns
Strategy: validation
Validate before calling
let (header_size, _) = parse_sqlite_varint(&page, payload_start)?;
assert!(payload_start + header_size as usize <= page.len(), "record header spills past page {root_page}; check page_size and overflow"); Prevention
- Only patch records that fit on the leaf page (no overflow)
- Confirm page_size matches PRAGMA page_size
- Validate page type byte (0x0D) before parsing the record
When it happens
Trigger: `patch_first_row_serial_type` computes `header_end = payload_start + header_size` from the parsed varint and it exceeds `bytes.len()` — caused by a malformed or non-leaf page, wrong page size, or overflow payload spilling off the page.
Common situations: Patching a row whose record spills to overflow pages, wrong page_size passed in, or the target page isn't the table leaf containing the first row.
Related errors
- payload start out of bounds on page
- record has no column
- serial types and do not store the same number of bytes
- unexpected serial type
- unknown integrity fixture path
AI-assisted analysis of tursodatabase/turso@8d4a589f8d (2026-09-20).
Data as JSON: /api/errors/c41057d2d5371eb9.
Report an issue: GitHub.
Appendix: source
Thrown at testing/sqltest/src/generator/mod.rs:1080
let payload_start = cell_start + payload_varint_len + rowid_varint_len;
anyhow::ensure!(
payload_start < bytes.len(),
"payload start out of bounds on page {root_page}"
);
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(())
}
View on GitHub (pinned to 8d4a589f8d)