{"record":{"id":"51fc8558a66efa3c","repo":"tursodatabase/turso","slug":"no-wal-to-checkpoint","errorCode":null,"errorMessage":"No WAL to checkpoint","messagePattern":"No WAL to checkpoint","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"core/mvcc/database/checkpoint_state_machine.rs","lineNumber":1485,"sourceCode":"        let boundary = if self.mode.should_restart_log() {\n            u64::MAX\n        } else {\n            self.durable_txid_max_new\n        };\n        let (c, outcome) = self.mvstore.storage.truncate(boundary)?;\n        if self.mode.should_restart_log() {\n            turso_assert!(\n                matches!(outcome, LogicalLogTruncateOutcome::Truncated),\n                \"TRUNCATE checkpoint must clear the logical log\"\n            );\n        }\n        Ok(c)\n    }\n\n    /// Perform a TRUNCATE checkpoint on the WAL\n    fn checkpoint_wal(&self) -> IOResultOr<CheckpointResult> {\n        let Some(wal) = &self.pager.wal else {\n            panic!(\"No WAL to checkpoint\");\n        };\n        match wal.checkpoint(&self.pager, self.mode, self.sync_mode)? {\n            IOResult::Done(result) => Ok(IOResult::Done(result)),\n            IOResult::IO(io) => Ok(IOResult::IO(io)),\n        }\n    }\n\n    /// Publish `nbackfills` after Passive backfill + DB sync. Skip Truncate/Restart.\n    /// Leaves the checkpoint guard held until Finalize (same as the pager).\n    fn publish_wal_backfill_if_needed(&mut self) {\n        if self.mode.should_restart_log() {\n            return;\n        }\n        let Some(result) = self.checkpoint_result.as_ref() else {\n            return;\n        };\n        if result.wal_checkpoint_backfilled == 0 {\n            return;","sourceCodeStart":1467,"sourceCodeEnd":1503,"githubUrl":"https://github.com/tursodatabase/turso/blob/492c4a71cd7c2649e7df83da1471b74f4b1c7aa9/core/mvcc/database/checkpoint_state_machine.rs#L1467-L1503","documentation":"checkpoint_wal in the MVCC checkpoint state machine performs a TRUNCATE checkpoint but panics if self.pager.wal is None, i.e. the pager was opened without a WAL. The state machine should only run when a WAL exists, so hitting this panic means the checkpoint driver was started on a non-WAL (or already torn down) pager.","triggerScenarios":"Driving the MVCC checkpoint state machine on a pager opened without WAL support, or after the WAL has been detached/closed while checkpointing is still in progress.","commonSituations":"Running MVCC-mode experiments with a journal mode that isn't WAL; shutting down a database while a background checkpoint is scheduled; engine development/fuzzing of the checkpoint path.","solutions":["Ensure the database is opened in WAL journal mode before triggering MVCC checkpointing.","Avoid closing/destroying the database while a checkpoint is still pending; complete or cancel checkpointing first.","File a bug with reproduction steps if this occurs in normal WAL-mode operation.","If developing the engine, add a guard so the checkpoint state machine is never entered without a WAL."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Only schedule MVCC checkpointing when the pager actually has a WAL\nif (db.pager.wal.is_some()) {\n    checkpoint_driver.start();\n}","typeGuard":null,"tryCatchPattern":"// Rust panic: prevent entry into the state machine rather than catching it\nassert!(pager.wal.is_some(), \"checkpoint requires WAL mode\");","preventionTips":["Open MVCC databases in WAL journal mode.","Complete or cancel pending checkpoints before closing the database.","Don't detach/close the WAL while background checkpoint tasks are scheduled."],"tags":["rust","mvcc","wal","checkpoint","internal-panic"],"backgroundTag":"internal-invariant-violation","analyzedSha":"492c4a71cd7c2649e7df83da1471b74f4b1c7aa9","analyzedAt":"2026-09-06T07:15:26.990Z","contentChangedAt":"2026-09-06T07:15:26.990Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}