{"record":{"id":"f32a06980980d9fd","repo":"JuliusBrussee/caveman","slug":"w-inspect-database-s-v","errorCode":null,"errorMessage":"%w: inspect database%s: %v","messagePattern":"%w: inspect database(.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"engine/ccr/store_generation.go","lineNumber":64,"sourceCode":"\t\treturn files, fmt.Errorf(\"%w: database parent changed\", ErrStorageChanged)\n\t}\n\tinfo, err := os.Stat(parent)\n\tif errors.Is(err, os.ErrNotExist) {\n\t\treturn files, fmt.Errorf(\"%w: database parent changed\", ErrStorageChanged)\n\t}\n\tif err != nil {\n\t\treturn files, fmt.Errorf(\"%w: %w: inspect database parent: %v\", ErrStorageChanged, errStorageUnverifiable, err)\n\t}\n\tif err := validateSQLiteParentSecurity(parent, info); err != nil {\n\t\treturn files, fmt.Errorf(\"%w: %w: %v\", ErrStorageChanged, errStorageUnverifiable, err)\n\t}\n\tfor i, suffix := range sqliteSuffixes {\n\t\tinfo, err := inspectSQLiteFile(path + suffix)\n\t\tif errors.Is(err, os.ErrNotExist) && i != 0 {\n\t\t\tcontinue\n\t\t}\n\t\tif errors.Is(err, os.ErrNotExist) {\n\t\t\treturn files, fmt.Errorf(\"%w: inspect database%s: %v\", ErrStorageChanged, suffix, err)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn files, fmt.Errorf(\"%w: %w: inspect database%s: %v\", ErrStorageChanged, errStorageUnverifiable, suffix, err)\n\t\t}\n\t\tif !info.Mode().IsRegular() {\n\t\t\treturn files, fmt.Errorf(\"%w: refusing non-regular database%s\", ErrStorageChanged, suffix)\n\t\t}\n\t\tfiles[i] = info\n\t}\n\treturn files, nil\n}\n\nfunc sameSQLiteFile(a, b os.FileInfo) bool {\n\tif a == nil || b == nil {\n\t\treturn a == nil && b == nil\n\t}\n\treturn os.SameFile(a, b)\n}","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/engine/ccr/store_generation.go#L46-L82","documentation":"The main database file (path + \"\" suffix) disappeared while its identity was being inspected. Because the main DB file must always exist for a valid store, ErrNotExist on it is treated as proof the storage changed — ErrStorageChanged is returned. In checkGeneration this error latches terminal quarantine: the Store is unusable for the rest of the process.","triggerScenarios":"The .db file itself (not -wal/-shm, which may legitimately be absent) is missing when a Store method runs or when OpenWithBudget inspects before opening — deleted by a cleanup tool, replaced by a rename, or removed while the connection stayed open.","commonSituations":"Disk cleanup deletes ~/.caveman/ccr.db while the agent runs; a deployment script recreates the database file; the file was moved to another volume; the process was resumed from a snapshot where the file no longer exists.","solutions":["Restore the database file at the original path (from backup) or let a fresh process recreate it: stop the process, then reopen.","Do not try to reuse the current Store — quarantine is latched; instantiate a new Store via Open/OpenWithBudget after the file situation is fixed.","Exclude the store directory from cleanup tools (bleachbit, tmpwatch, cron rm jobs).","If the database was deliberately moved, update the store path configuration and restart.","Check for concurrent processes deleting/racing on the same ccr.db path and serialize them."],"exampleFix":"// before\nstore.Put(rec) // ccr.db deleted underneath -> quarantined\n// after\nif _, err := os.Stat(dbPath); err != nil {\n    store.Close()\n    store, _ = ccr.Open(dbPath)\n}\nstore.Put(rec)","handlingStrategy":"try-catch","validationCode":"if _, err := os.Stat(dbPath); err != nil {\n    // db missing: reopen fresh before calling the store\n    store.Close(); store, _ = ccr.Open(dbPath)\n}","typeGuard":null,"tryCatchPattern":"if errors.Is(err, ccr.ErrStorageChanged) {\n    // quarantine is terminal: replace the Store\n    store, _ = ccr.Open(dbPath) // new process-level store\n}","preventionTips":["Exclude the store directory from disk-cleanup jobs (tmpwatch, cron rm)","Do not delete/recreate ccr.db under a running process","Restore databases only while the engine is stopped","Monitor for parallel processes sharing the same store path"],"tags":["go","sqlite","file-not-found","storage-changed","quarantine"],"backgroundTag":"file-not-found","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"}