tursodatabase/turso · critical

page evicted between scan and prepare

Error message

page evicted between scan and prepare

What it means

Commit preparation walks commit_info.page_sources; entries recorded as PageSource::Cached(page_id) are re-fetched from the page cache with expect("page evicted between scan and prepare"), relying on pages being pinned between the commit scan and frame preparation. The panic fires when such a page is no longer in the cache - an eviction raced the commit.

Source

Thrown at core/storage/pager.rs:4552

                    let page_sz = self.get_page_size_unchecked();
                    let mut commit_info = self.commit_info.write();
                    let mut cache = self.page_cache.write();

                    'inner: loop {
                        let cursor = commit_info.page_source_cursor;
                        if cursor >= commit_info.page_sources.len() {
                            break 'inner;
                        }

                        let total = commit_info.page_sources.len();
                        let is_last = cursor + 1 >= total;
                        // Linear consumption, no lookup required
                        let page = match &commit_info.page_sources[cursor] {
                            PageSource::Cached(page_id) => {
                                let page_key = PageCacheKey::new(*page_id);
                                cache
                                    .get(&page_key)?
                                    .expect("page evicted between scan and prepare")
                            }
                            PageSource::Evicted(page) => page.clone(),
                        };
                        // Defensive check: prepare_frames will read page contents,
                        // which panics if the buffer is not loaded. If we got here
                        // with an unloaded page (e.g. an evicted dirty page whose
                        // backing WAL frame was truncated by a savepoint rollback),
                        // surface an internal error instead of panicking.
                        if !page.is_loaded() {
                            return Err(LimboError::InternalError(format!(
                                "dirty page {} has no buffer loaded at commit time",
                                page.get().id()
                            ))
                            .into());
                        }
                        turso_assert!(
                            page.get().overflow_cells.is_empty(),
                            "dirty page still has overflow cells at commit time",

View on GitHub (pinned to 492c4a71cd)

Solutions

  1. Report to Turso with a reproduction - pin-discipline bug, not caller-fixable
  2. Raise the page cache size so the commit's dirty set fits without eviction pressure
  3. Commit in smaller batches to shrink the page_sources list
  4. Run PRAGMA integrity_check after any occurrence to confirm the database is intact
Defensive patterns

Strategy: try-catch

Validate before calling

// Application-level: keep the per-transaction dirty set comfortably below the
// page cache so the commit scan cannot lose pages to eviction:
//   - raise cache size (PRAGMA cache_size / engine config), or
//   - commit in smaller batches.
conn.execute("PRAGMA cache_size = -262144", ())?; // example: ~256MB cache

Try / catch

let result = std::panic::catch_unwind(AssertUnwindSafe(|| conn.execute("COMMIT", ())));
if result.is_err() {
    conn.close().ok();
    // Uncommitted work is lost by design; reopen and verify:
    //   PRAGMA integrity_check;
}

Prevention

When it happens

Trigger: Committing a large dirty set while cache pressure evicts a page that was recorded as cached; or pin-count bookkeeping dropped a pin early (a statement released pages mid-commit). The neighboring unloaded-page check converts the milder variant into an InternalError, but the cache miss itself panics here.

Common situations: Big transactions near the cache limit, concurrent statements finishing during commit, engine regressions in pin discipline.

Related errors


AI-assisted analysis of tursodatabase/turso@492c4a71cd (2026-08-20). Data as JSON: /api/errors/8ecb90550d6a0731. Report an issue: GitHub.