{"record":{"id":"828d268580520ad7","repo":"JuliusBrussee/caveman","slug":"w-refusing-non-regular-database-s","errorCode":null,"errorMessage":"%w: refusing non-regular database%s","messagePattern":"%w: refusing non-regular database(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"engine/ccr/store_generation.go","lineNumber":70,"sourceCode":"\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 {\n\tfor i := range a {\n\t\tif a[i] != nil && !sameSQLiteFile(a[i], b[i]) {\n\t\t\treturn false","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/engine/ccr/store_generation.go#L52-L88","documentation":"A database file (main, -wal, or -shm) exists at inspection time but is not a regular file — e.g. it is a symlink, directory, FIFO, or device node. SQLite safety guarantees assume regular files, so inspectSQLiteGeneration refuses with ErrStorageChanged. This is a hard identity/security failure; in checkGeneration it latches terminal quarantine for the Store.","triggerScenarios":"path, path+\"-wal\", or path+\"-shm\" was replaced by a non-regular filesystem object while the store was open, or such an object was present at open time: someone symlinked ccr.db to another location, created a directory named ccr.db-wal, or placed a named pipe where a file belongs.","commonSituations":"Dotfile managers symlink ~/.caveman/ccr.db into a synced folder; a build/test script mkdir's a path colliding with the WAL sidecar; an attacker or accident swaps in a symlink to redirect writes.","solutions":["Replace the non-regular object with a real file: delete the symlink/directory/FIFO and let a fresh Store create the database (or restore from backup).","Restart the process with a new Store — quarantine is latched and the old connection is intentionally never reused.","Configure your dotfile/sync manager to symlink the whole ~/.caveman directory (whose parent is checked) rather than individual database files, or use mounts instead of per-file symlinks.","Audit for path collisions: ensure no script creates ccr.db, ccr.db-wal, or ccr.db-shm as directories or special files.","Keep the store in a user-owned directory that external tools do not manage."],"exampleFix":"# before\nln -s /sync/ccr.db ~/.caveman/ccr.db\n# after\nmv /sync/ccr.db ~/.caveman/ccr.db   # real file in place of symlink","handlingStrategy":"validation","validationCode":"for _, sfx := range []{\"\", \"-wal\", \"-shm\"} {\n    if fi, err := os.Lstat(dbPath + sfx); err == nil && !fi.Mode().IsRegular() {\n        return fmt.Errorf(\"%s is not a regular file\", dbPath+sfx)\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never symlink ccr.db or its sidecars; symlink or mount the whole directory instead","Ensure build/test scripts never mkdir files named ccr.db-wal or ccr.db-shm","Use os.Lstat checks in deployment preflight to detect swapped special files","Keep the store in a directory managed only by the engine"],"tags":["go","sqlite","symlink","file-type","quarantine"],"backgroundTag":"incompatible-source-type","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"}