tursodatabase/turso · critical

result should be set

Error message

result should be set

What it means

Turso's pager runs WAL checkpoints as an async state machine. After a checkpoint's sync phase completes, next_post_sync_checkpoint_phase (core/storage/pager.rs:4583) locks the shared checkpoint state and unwraps state.result (a CheckpointResult stored in the state struct, pager.rs:1035) with expect("result should be set"). The panic means the pager tried to compute the next checkpoint phase while checkpoint_state.result was None - an internal invariant violation, typically a race where the checkpoint state was reset (e.g. by cleanup_after_checkpoint_failure -> reset_checkpoint_state after an I/O error) while a post-sync phase transition at pager.rs:4813 was still in flight.

Source

Thrown at core/storage/pager.rs:4858

        state.lock_source = CheckpointLockSource::Acquire;
    }

    /// Clean up after a auto-checkpoint failure.
    /// Auto-checkpoint executed outside of the main transaction - so WAL transaction was already finalized
    pub fn cleanup_after_auto_checkpoint_failure(&self) {
        self.cleanup_after_checkpoint_failure();
    }

    pub fn cleanup_after_checkpoint_failure(&self) {
        self.reset_checkpoint_state();
        if let Some(wal) = self.wal.as_ref() {
            wal.abort_checkpoint();
        }
    }

    fn next_post_sync_checkpoint_phase(&self, clear_page_cache: bool) -> CheckpointPhase {
        let state = self.checkpoint_state.read();
        let result = state.result.as_ref().expect("result should be set");
        let mode = state.mode.expect("mode should be set");
        if result.wal_checkpoint_backfilled > 0
            && !matches!(
                mode,
                CheckpointMode::Restart | CheckpointMode::Truncate { .. }
            )
        {
            // if we are using a custom codec, then we might have to read the whole page 1 so that
            // it can be decoded. Otherwise reading the header is enough.
            let read_page = self.io_ctx.read().has_codec_transform();
            let read_size = if read_page {
                self.get_page_size_unchecked().get() as usize
            } else {
                PageSize::MIN as usize
            };
            return CheckpointPhase::ReadDbIdentity {
                clear_page_cache,
                read: PendingCheckpointDbIdentityRead {

View on GitHub (pinned to 492c4a71cd)

Solutions

  1. Re-run with RUST_BACKTRACE=1 to capture the full panic trace and note the exact sequence of checkpoint calls, then report it as a Turso engine bug with a minimal reproduction - this is an internal state-machine bug, not caller error.
  2. Upgrade to the latest tursodb release; the checkpoint state machine is actively developed and the race may already be fixed.
  3. Serialize checkpoints: let a single connection run PRAGMA wal_checkpoint instead of several connections checkpointing concurrently.
  4. If it reproduces on an encrypted/codec database or after an injected I/O failure, include that detail in the bug report (both branches are visible in the code path).

Example fix

-- before: multiple connections checkpointing concurrently
-- conn A: PRAGMA wal_checkpoint(TRUNCATE);
-- conn B: PRAGMA wal_checkpoint(TRUNCATE);

-- after: single dedicated connection, others just write
-- checkpoint conn: PRAGMA wal_checkpoint(TRUNCATE);
Defensive patterns

Strategy: try-catch

Try / catch

Rust host: wrap the checkpoint call in std::panic::catch_unwind (AssertUnwindSafe) to contain the engine panic, then treat the checkpoint as failed and retry later. Python/JS bindings: catch the RuntimeError the panic is surfaced as around `PRAGMA wal_checkpoint` execution.

Prevention

When it happens

Trigger: Issuing PRAGMA wal_checkpoint (especially RESTART/TRUNCATE/FULL) that reaches the post-sync phase transition at pager.rs:4813 after checkpoint state was reset by cleanup_after_checkpoint_failure(); concurrent wal_checkpoint calls from multiple connections racing an abort; checkpoints on databases where wal_checkpoint_backfilled > 0, which takes the page-1 re-read branch when the DB has a codec transform (has_codec_transform).

Common situations: Concurrent checkpointing under load, checkpoint retry after a transient I/O error, encrypted/codec-wrapped databases, custom VFS with fault injection, WAL stress or MVCC test workloads, engine version skew after a checkpoint-state-machine refactor.

Related errors


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