{"record":{"id":"557a34cb28bda415","repo":"JuliusBrussee/caveman","slug":"sqlite-driver-lacks-persistent-journal-control","errorCode":null,"errorMessage":"sqlite driver lacks persistent journal control","messagePattern":"sqlite driver lacks persistent journal control","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"engine/ccr/store_generation.go","lineNumber":115,"sourceCode":"\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n// PERSIST_WAL prevents close from unlinking WAL/SHM by their original names.\n// It DOES NOT prevent a final checkpoint. A changed generation is quarantined\n// regardless of this flag; ordinary closes preserve the valid journal files.\nfunc persistSQLiteWAL(db *sql.DB) error {\n\tconn, err := db.Conn(context.Background())\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer conn.Close()\n\treturn conn.Raw(func(raw any) error {\n\t\tcontrol, ok := raw.(sqlite.FileControl)\n\t\tif !ok {\n\t\t\treturn errors.New(\"sqlite driver lacks persistent journal control\")\n\t\t}\n\t\t_, err := control.FileControlPersistWAL(\"main\", 1)\n\t\treturn err\n\t})\n}\n\n// A failed initialization may already hold journals whose paths were replaced\n// before the first complete generation snapshot. Never close an opened disk\n// connection on that error path: SQLite close can checkpoint its stale WAL into\n// the current main file. The process must exit to release those descriptors.\nfunc closeSQLiteAfterOpenFailure(db *sql.DB, path string) {\n\tif path == \":memory:\" || db.Stats().OpenConnections == 0 {\n\t\t_ = db.Close()\n\t}\n}\n\n// checkGeneration runs with mu held. A detected generation change is terminal\n// for this Store. A fresh process must open the restored/current database;","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/engine/ccr/store_generation.go#L97-L133","documentation":"The CCR generation guard upgrades a SQLite database to persistent-WAL journal mode by calling FileControlPersistWAL through a raw driver connection. That call requires the driver to implement the sqlite.FileControl interface. When the registered driver does not expose journal control (a different or older SQLite driver was injected), the store fails closed rather than silently losing the WAL-persistence guarantee that generation quarantining depends on.","triggerScenarios":"Store setup/verification path calls conn.Raw(func(raw any) error { control, ok := raw.(sqlite.FileControl); ... }) and the type assertion fails because the *sql.DB was opened with a SQLite driver that does not implement sqlite.FileControl (e.g. mattn/go-sqlite3, an older modernc.org/sqlite, or a custom driver.Driver registered under the same name).","commonSituations":"A dependency or test registered a different SQLite driver before this library imported its own; upgrading or pinning an older modernc.org/sqlite version lacking FileControlPersistWAL; building with a driver-swap build tag; running in an environment where the import of the expected driver was pruned.","solutions":["Import the supported sqlite driver (the one providing sqlite.FileControl) and ensure it registers first; remove conflicting sqlite driver imports.","Pin/upgrade modernc.org/sqlite to a version that implements FileControl and FileControlPersistWAL.","Check go.mod for duplicate sqlite drivers and consolidate on the library's supported driver.","If you must use a driver without FileControl, disable the WAL-persistence generation guard explicitly rather than passing the connection in."],"exampleFix":"// before\nimport _ \"github.com/mattn/go-sqlite3\"\ndb, _ := sql.Open(\"sqlite\", path)\n// after\nimport _ \"modernc.org/sqlite\" // driver exposing sqlite.FileControl\ndb, _ := sql.Open(\"sqlite\", path)","handlingStrategy":"validation","validationCode":"// ensure the expected driver is linked\nimport _ \"modernc.org/sqlite\"\n// optionally probe:\ndb, err := sql.Open(\"sqlite\", path)\nif err != nil { return err }\nerr = db.Raw(func(raw any) error {\n    if _, ok := raw.(sqlite.FileControl); !ok {\n        return errors.New(\"driver lacks FileControl\")\n    }\n    return nil\n})","typeGuard":"func hasFileControl(raw any) bool { _, ok := raw.(sqlite.FileControl); return ok }","tryCatchPattern":null,"preventionTips":["Import only the supported sqlite driver; avoid driver name collisions in go.mod","Pin modernc.org/sqlite to a version implementing FileControlPersistWAL","Add a startup smoke test asserting WAL persistence control works"],"tags":["sqlite","driver-incompatibility","configuration"],"backgroundTag":"missing-dependency","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}