{"record":{"id":"46f2993cdd736645","repo":"tursodatabase/turso","slug":"preparingwalstart-requires-a-wal","errorCode":null,"errorMessage":"PreparingWalStart requires a WAL","messagePattern":"PreparingWalStart requires a WAL","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"core/storage/pager.rs","lineNumber":4188,"sourceCode":"                                let completions = self.spill_pages_to_disk(&pages, &mut group)?;\n                                if completions.is_empty() {\n                                    self.finish_ephemeral_spill(&pages);\n                                    return Ok(IOResult::Done(()));\n                                }\n                                *self.spill_state.write() =\n                                    SpillState::WritingToDisk { pages, completions };\n                                io_yield_one!(group.build());\n                            }\n                        }\n                    }\n                }\n                SpillState::PreparingWalStart { pages, completion } => {\n                    if !completion.succeeded() {\n                        io_yield_one!(completion);\n                    }\n                    // Header (and any truncate) durable — issue the fsync that\n                    // marks the WAL initialized.\n                    let wal = self.wal.as_ref().expect(\"PreparingWalStart requires a WAL\");\n                    let finish_c = wal.prepare_wal_finish(self.get_sync_type())?;\n                    *self.spill_state.write() = SpillState::PreparingWalFinish {\n                        pages,\n                        completion: finish_c,\n                    };\n                    continue;\n                }\n                SpillState::PreparingWalFinish { pages, completion } => {\n                    if !completion.succeeded() {\n                        io_yield_one!(completion);\n                    }\n                    // WAL is now initialized; append the spill frames.\n                    return self.spill_append_frames_to_wal(pages);\n                }\n                SpillState::WritingToWal { pages, completions } => {\n                    for c in &completions {\n                        if !c.succeeded() {\n                            io_yield_one!(c.clone());","sourceCodeStart":4170,"sourceCodeEnd":4206,"githubUrl":"https://github.com/tursodatabase/turso/blob/492c4a71cd7c2649e7df83da1471b74f4b1c7aa9/core/storage/pager.rs#L4170-L4206","documentation":"Cache-spill state machine: after the WAL header write (SpillState::PreparingWalStart) is durable, the pager issues the fsync that marks the WAL initialized via self.wal.as_ref().expect(\"PreparingWalStart requires a WAL\"). Reaching this state on a pager with no WAL attached (self.wal == None) panics.","triggerScenarios":"Cache spill triggered (dirty pages exceed the page cache) on a database opened without a WAL - in-memory databases or a journal configuration where Pager::wal was never set - so the spill state machine advances into WAL-dependent states.","commonSituations":"Large single statements on :memory: databases overflowing the cache, engine regressions allowing spill without WAL, custom open paths producing a WAL-less pager.","solutions":["Report to Turso: spill-without-WAL should be rejected with an error, not a panic","Open file-backed databases in WAL mode when large write statements are expected","Increase the page cache so the dirty set fits without spilling","In embedder code, disable spilling explicitly on WAL-less databases with pager.set_spill_enabled(false)"],"exampleFix":"// before: in-memory database, spill enabled by default under cache pressure\nlet db = Database::open_file(io, \":memory:\", false)?; // no WAL\n// huge INSERT ... overwhelms cache -> spill -> panic\n\n// after: keep dirty set within cache / disable spill on WAL-less pagers\npager.set_spill_enabled(false);\n// and/or commit in smaller batches","handlingStrategy":"validation","validationCode":"// For WAL-less databases (in-memory or otherwise), keep the dirty set below\n// the cache so spill never triggers, or disable spill explicitly in embedder code:\nif !pager.has_wal_enabled_for_your_setup() {\n    pager.set_spill_enabled(false);\n}\n// Application level: open file databases in WAL mode before large writes:\nconn.execute(\"PRAGMA journal_mode=WAL\", ())?;","typeGuard":null,"tryCatchPattern":"let result = std::panic::catch_unwind(AssertUnwindSafe(|| do_big_write(&conn)));\nif result.is_err() { conn.close().ok(); /* reopen in WAL mode or raise cache size */ }","preventionTips":["Use WAL journal mode for file databases that will run large write statements","Size the page cache above your largest expected dirty set for in-memory databases","Commit in smaller batches to stay under the spill threshold","Treat occurrence as an engine bug (spill without WAL) and report it"],"tags":["pager","cache-spill","wal","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-14T00:17:10.932Z"}