{"record":{"id":"d7732228e35bea69","repo":"JuliusBrussee/caveman","slug":"w-w-inspect-database-s-v","errorCode":null,"errorMessage":"%w: %w: inspect database%s: %v","messagePattern":"%w: %w: inspect database(.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"engine/ccr/store_generation.go","lineNumber":67,"sourceCode":"\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}\n\n// sameExisting also permits sidecars to be created while opening a clean DB.\nfunc (a sqliteGeneration) sameExisting(b sqliteGeneration) bool {","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/engine/ccr/store_generation.go#L49-L85","documentation":"Inspecting one of the database files (main, -wal, or -shm) failed with an error other than ErrNotExist. The library cannot confirm file identity, so it returns ErrStorageChanged wrapped with errStorageUnverifiable. Per checkGeneration, unverifiable failures fail only the current operation — the store is not terminally quarantined — but the call does not proceed.","triggerScenarios":"inspectSQLiteFile returns a non-ErrNotExist error for path, path+\"-wal\", or path+\"-shm\": permission errors on an individual file, EIO on a network volume, a file lock held by another process, or a stat failure caused by a path component change.","commonSituations":"WAL sidecar made unreadable by an ACL change; another SQLite process holds an exclusive lock on the -wal file; corrupted directory entry on a failing disk; network home briefly offline mid-operation.","solutions":["Fix permissions/ownership on the specific file named in the error message.","Resolve competing access: close other processes holding the DB/WAL open, or move to a per-user store path to avoid cross-process contention.","Check disk/filesystem health if the error is an I/O error; fsck or remount the volume.","Retry once conditions clear — this error is not latched, later calls may succeed.","Exclude the store directory from antivirus/indexer interference if locks recur."],"exampleFix":"// before\n-rw-r----- ccr.db -rw------- ccr.db-wal (mixed owners)\n// after\nchown \"$USER\" ~/.caveman/ccr.db* && chmod 600 ~/.caveman/ccr.db*","handlingStrategy":"retry","validationCode":"for _, sfx := range []{\"\", \"-wal\", \"-shm\"} {\n    if _, err := os.Stat(dbPath + sfx); err != nil && !os.IsNotExist(err) {\n        return fmt.Errorf(\"%s not inspectable: %v\", dbPath+sfx, err)\n    }\n}","typeGuard":null,"tryCatchPattern":"if err != nil && errors.Is(err, ccr.ErrStorageChanged) && !storePermanentlyDead(err) {\n    time.Sleep(500 * time.Millisecond)\n    err = store.Put(rec) // unverifiable failures are not latched; retry\n}","preventionTips":["Keep per-file permissions consistent (0600, same owner) on ccr.db*","Avoid NFS/removable volumes for the store","Close other SQLite processes before operating on the same files","Exclude the directory from indexers and AV that lock files"],"tags":["go","sqlite","filesystem","wal","transient"],"backgroundTag":"file-read-failed","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"}