JuliusBrussee/caveman · critical
: refusing non-regular database
Error message
%w: refusing non-regular database%s
What it means
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.
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.
Example fix
# before ln -s /sync/ccr.db ~/.caveman/ccr.db # after mv /sync/ccr.db ~/.caveman/ccr.db # real file in place of symlink
Defensive patterns
Strategy: validation
Validate before calling
for _, sfx := range []{"", "-wal", "-shm"} {
if fi, err := os.Lstat(dbPath + sfx); err == nil && !fi.Mode().IsRegular() {
return fmt.Errorf("%s is not a regular file", dbPath+sfx)
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.
Related errors
- refusing non-regular file
- : database or journal removed or replaced; connection…
- : database parent changed
- : inspect database
- : : inspect database parent
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/828d268580520ad7.
Report an issue: GitHub.
Appendix: source
Thrown at engine/ccr/store_generation.go:70
if err != nil {
return files, fmt.Errorf("%w: %w: inspect database parent: %v", ErrStorageChanged, errStorageUnverifiable, err)
}
if err := validateSQLiteParentSecurity(parent, info); err != nil {
return files, fmt.Errorf("%w: %w: %v", ErrStorageChanged, errStorageUnverifiable, err)
}
for i, suffix := range sqliteSuffixes {
info, err := inspectSQLiteFile(path + suffix)
if errors.Is(err, os.ErrNotExist) && i != 0 {
continue
}
if errors.Is(err, os.ErrNotExist) {
return files, fmt.Errorf("%w: inspect database%s: %v", ErrStorageChanged, suffix, err)
}
if err != nil {
return files, fmt.Errorf("%w: %w: inspect database%s: %v", ErrStorageChanged, errStorageUnverifiable, suffix, err)
}
if !info.Mode().IsRegular() {
return files, fmt.Errorf("%w: refusing non-regular database%s", ErrStorageChanged, suffix)
}
files[i] = info
}
return files, nil
}
func sameSQLiteFile(a, b os.FileInfo) bool {
if a == nil || b == nil {
return a == nil && b == nil
}
return os.SameFile(a, b)
}
// sameExisting also permits sidecars to be created while opening a clean DB.
func (a sqliteGeneration) sameExisting(b sqliteGeneration) bool {
for i := range a {
if a[i] != nil && !sameSQLiteFile(a[i], b[i]) {
return falseView on GitHub (pinned to 3ee70a1026)