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

  1. Restore the database and WAL as a consistent pair from a known-good backup
  2. Recreate the sync server database from the authoritative source of truth
  3. Stop the server before copying or mutating db/WAL files
  4. 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

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


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)