From 7b1c0342b496beef335db354a727ad750f9bc6d0 Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Tue, 11 Aug 2026 16:54:50 -0600 Subject: [PATCH] fix(rag): delete the DuckDB write-ahead log alongside the store Deleting a RAG removed its .duckdb file but left the sibling .duckdb.wal behind. DuckDB only removes that log on a clean close, so any RAG whose process was killed left one on disk, and creating a new RAG under the same name let it inherit a write-ahead log describing someone else's data. The test helper already cleaned the log up after itself, which is why no test noticed the production path did not. --- src/config/paths.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/config/paths.rs b/src/config/paths.rs index f49f688..bbbf6b1 100644 --- a/src/config/paths.rs +++ b/src/config/paths.rs @@ -437,6 +437,14 @@ pub(crate) fn remove_rag_sidecars(dir: &Path, name: &str) -> Result<()> { if duckdb_path.exists() { let _ = remove_file(&duckdb_path); } + // DuckDB keeps its write-ahead log in a sibling file and only removes it on a + // clean close, so a crash or a kill leaves one behind. Deleting the database + // without it strands a `.wal` that the next RAG created under the same name + // would inherit as if it were its own. + let wal_path = dir.join(format!("{name}.duckdb.wal")); + if wal_path.exists() { + let _ = remove_file(&wal_path); + } let mixin_path = dir.join(format!("{name}.sbx-mixin.yaml")); if mixin_path.exists() { remove_file(&mixin_path).with_context(|| { @@ -894,16 +902,20 @@ mod tests { } #[test] - fn remove_rag_sidecars_removes_both() { + fn remove_rag_sidecars_removes_duckdb_wal_and_mixin() { let root = sidecar_temp_dir("rag-sidecars-both"); let duckdb = root.join("docs.duckdb"); + // DuckDB leaves this behind whenever it was not closed cleanly. + let wal = root.join("docs.duckdb.wal"); let mixin = root.join("docs.sbx-mixin.yaml"); fs::write(&duckdb, "db").unwrap(); + fs::write(&wal, "wal").unwrap(); fs::write(&mixin, "mixin").unwrap(); remove_rag_sidecars(&root, "docs").unwrap(); assert!(!duckdb.exists(), "the .duckdb sidecar must be removed"); + assert!(!wal.exists(), "the .duckdb.wal sidecar must be removed"); assert!( !mixin.exists(), "the .sbx-mixin.yaml sidecar must be removed"