{"record":{"id":"afb27e85718e4bae","repo":"tursodatabase/turso","slug":"spill-append-frames-to-wal-requires-a-wal","errorCode":null,"errorMessage":"spill_append_frames_to_wal requires a WAL","messagePattern":"spill_append_frames_to_wal requires a WAL","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"core/storage/pager.rs","lineNumber":4275,"sourceCode":"                        \"try_spill_dirty_pages: successfully spilled {} pages to disk\",\n                        pages.len()\n                    );\n                    return Ok(IOResult::Done(()));\n                }\n            }\n        }\n    }\n\n    /// Append the prepared spill `pages` as WAL frames. Returns `Done` if\n    /// the write completed synchronously, otherwise transitions to\n    /// `SpillState::WritingToWal` and yields the write completion. The WAL\n    /// must already be initialized (callers route through `PreparingWal*`\n    /// first).\n    fn spill_append_frames_to_wal(&self, pages: Vec<PinGuard>) -> IOResultOr<()> {\n        let wal = self\n            .wal\n            .as_ref()\n            .expect(\"spill_append_frames_to_wal requires a WAL\");\n        let page_sz = self.get_page_size().unwrap_or_default();\n        let wal_pages: Vec<PageRef> = pages\n            .iter()\n            .map(|p| -> Result<PageRef> {\n                self.subjournal_page_if_required(p)?;\n                // Set write_pending on all pages before WAL write so callback can\n                // detect mid-write modifications.\n                p.set_write_pending();\n                Ok(p.to_page())\n            })\n            .collect::<Result<Vec<_>>>()?;\n        let c = wal.append_frames_vectored(wal_pages, page_sz)?;\n\n        if c.succeeded() {\n            // Synchronous completion, WAL tags already set by callback.\n            {\n                let mut cache = self.page_cache.write();\n                for page in &pages {","sourceCodeStart":4257,"sourceCodeEnd":4293,"githubUrl":"https://github.com/tursodatabase/turso/blob/492c4a71cd7c2649e7df83da1471b74f4b1c7aa9/core/storage/pager.rs#L4257-L4293","documentation":"Pager::spill_append_frames_to_wal() appends the prepared spill pages as WAL frames and requires self.wal with expect(\"spill_append_frames_to_wal requires a WAL\"). Callers are supposed to route through the PreparingWal* states first, which initialize the WAL; reaching this function with no WAL attached breaks that ordering.","triggerScenarios":"A cache spill that skips or fails through the PreparingWalStart/PreparingWalFinish initialization states and lands directly in frame append on a pager whose WAL is absent (in-memory database, WAL-less configuration) or was torn down mid-spill.","commonSituations":"Memory databases under heavy write load, engine regressions in spill state ordering, WAL teardown racing a spill (close during spill).","solutions":["Report to Turso with the workload - the spill state machine reached append without WAL init","Avoid overwhelming the page cache on WAL-less (e.g. in-memory) databases: raise cache size or batch commits","Disable cache spill on such databases (pager.set_spill_enabled(false))","Ensure the connection is not being closed concurrently with a long write statement"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Same guard as the spill-entry point: ensure WAL is in play or spill is off\n// before heavy writes on WAL-less databases:\npager.set_spill_enabled(false); // embedder code for :memory: databases\n// Application level: keep single-statement write volume under the cache size,\n// or use journal_mode=WAL for file databases.","typeGuard":null,"tryCatchPattern":"let result = std::panic::catch_unwind(AssertUnwindSafe(|| do_big_insert(&conn)));\nif result.is_err() { conn.close().ok(); /* reopen, split the write into batches */ }","preventionTips":["Avoid overwhelming the page cache on in-memory/WAL-less databases","Do not close connections concurrently with long-running write statements (WAL teardown can race spill)","Batch large INSERT/UPDATE statements with intermediate commits","Report spill-state-machine ordering panics upstream"],"tags":["pager","cache-spill","wal","state-machine","panic"],"backgroundTag":"wal-not-initialized","analyzedSha":"492c4a71cd7c2649e7df83da1471b74f4b1c7aa9","analyzedAt":"2026-08-20T07:02:18.389Z","contentChangedAt":"2026-08-20T07:02:18.389Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}