tursodatabase/turso · critical
database page is missing from replace-base snapshot
Error message
database page {} is missing from replace-base snapshot What it means
While reading a replace-base snapshot, the server loops over pages 1..=db_size but try_wal_watermark_read_page reported a page as not found in the database file or the WAL up to the frame watermark. The snapshot's declared size and the actually readable pages disagree, meaning the db+WAL file set is incomplete, truncated, or corrupt.
Solutions
- Restore the database and WAL as a consistent pair from a known-good backup
- Recreate the sync server database from the authoritative source of truth
- Stop the server before copying or mutating db/WAL files
- Run integrity verification (dbhash / integrity check) before serving sync traffic
Defensive patterns
Strategy: fallback
Validate before calling
// pre-flight: claimed page count must be coverable by db+WAL bytes
let min_bytes = db_size_pages as u64 * PAGE_SIZE as u64;
let available = db_file_len + wal_file_len;
assert!(available >= min_bytes, "snapshot claims {db_size_pages} pages but files are too small"); Try / catch
match read_replace_base_pages().await {
Ok(pages) => pages,
Err(e) if e.to_string().contains("missing from replace-base snapshot") => {
quarantine_current_db().await?; // stop serving inconsistent state
rebuild_db_from_source().await?; // recreate from authoritative copy
read_replace_base_pages().await?
}
Err(e) => return Err(e),
} Prevention
- Always copy the db and its WAL together, with the server stopped
- Never hand-edit or truncate db/WAL files
- Verify consistency (dbhash / integrity check) before putting a file set into service
- Monitor disk space so writes do not fail mid-page
When it happens
Trigger: Serving a replace-base bootstrap when the db file is truncated relative to its claimed size; db and WAL files copied from different points in time; a concurrent checkpoint/truncation removing pages mid-read; hand-edited files.
Common situations: Copying a database without its WAL sidecar; crash mid-checkpoint; disk-full during earlier writes; test fixtures pairing a db with a stale WAL; copying files while the server is running.
Related errors
- database page number does not fit u32
- invalid MVCC logical log frame end magic at offset
- invalid MVCC logical log frame magic at offset
- invalid MVCC logical log header flags
- invalid MVCC logical log header length
AI-assisted analysis of tursodatabase/turso@492c4a71cd (2026-08-20).
Data as JSON: /api/errors/189011ba319631c9.
Report an issue: GitHub.
Appendix: source
Thrown at cli/sync_server.rs:978
#[allow(clippy::type_complexity)]
fn read_replace_base_pages(&self, db: &DbHandle) -> Result<(u64, Vec<(u64, Vec<u8>)>)> {
let conn = db.conn.lock().unwrap();
let wal_state = conn.wal_state()?;
let frame_watermark = Some(wal_state.max_frame);
let db_size = current_snapshot_db_size_pages(&conn, wal_state.max_frame)?;
let pages_capacity = usize::try_from(db_size)
.map_err(|_| anyhow!("database page count does not fit usize: {db_size}"))?;
let mut pages = Vec::with_capacity(pages_capacity);
for page_no in 1..=db_size {
let page_no_u32 = u32::try_from(page_no)
.map_err(|_| anyhow!("database page number does not fit u32: {page_no}"))?;
let mut page = vec![0; PAGE_SIZE];
let found =
conn.try_wal_watermark_read_page(page_no_u32, &mut page, frame_watermark)?;
if !found {
return Err(anyhow!(
"database page {} is missing from replace-base snapshot",
page_no
));
}
pages.push((page_no - 1, page));
}
Ok((db_size, pages))
}
}
struct HttpResponse {
status: u16,
content_type: String,
body: Vec<u8>,
}
struct MvccLogSnapshot {
end_offset: u64,View on GitHub (pinned to 492c4a71cd)