{"record":{"id":"5aef0877ec5d41ac","repo":"JuliusBrussee/caveman","slug":"secure-sqlite-q-w","errorCode":null,"errorMessage":"secure sqlite %q: %w","messagePattern":"secure sqlite %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"engine/ccr/store_sqlite.go","lineNumber":308,"sourceCode":"\t\treturn \"\", fmt.Errorf(\"resolve sqlite parent %q: %w\", parent, err)\n\t}\n\tresolvedParent, err := filepath.EvalSymlinks(absoluteParent)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"resolve sqlite parent %q: %w\", parent, err)\n\t}\n\tparentInfo, err := os.Stat(resolvedParent)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"inspect sqlite parent %q: %w\", resolvedParent, err)\n\t}\n\tif !parentInfo.IsDir() {\n\t\treturn \"\", fmt.Errorf(\"sqlite parent %q must be a directory\", resolvedParent)\n\t}\n\tif err := validateSQLiteParentSecurity(resolvedParent, parentInfo); err != nil {\n\t\treturn \"\", err\n\t}\n\tcanonicalPath := filepath.Join(resolvedParent, filepath.Base(path))\n\tif err := secureSQLiteFile(canonicalPath, true); err != nil {\n\t\treturn \"\", fmt.Errorf(\"secure sqlite %q: %w\", canonicalPath, err)\n\t}\n\tfor _, suffix := range []string{\"-wal\", \"-shm\"} {\n\t\tif err := secureSQLiteFile(canonicalPath+suffix, false); err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"secure sqlite sidecar %q: %w\", canonicalPath+suffix, err)\n\t\t}\n\t}\n\treturn canonicalPath, nil\n}\n\nfunc secureSQLiteFiles(path string) error {\n\tif path == \":memory:\" {\n\t\treturn nil\n\t}\n\tif _, err := inspectSQLiteGeneration(path); err != nil {\n\t\treturn err\n\t}\n\tfor _, suffix := range sqliteSuffixes {\n\t\tif err := secureSQLiteFile(path+suffix, false); err != nil {","sourceCodeStart":290,"sourceCodeEnd":326,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/engine/ccr/store_sqlite.go#L290-L326","documentation":"PrepareSQLitePathCanonical hardens the canonical sqlite file (and its WAL/SHM sidecars) via secureSQLiteFile, which enforces regular-file type and restrictive permissions (e.g. chmod 0600). This error wraps any failure of that hardening step, such as permission errors changing the file mode or I/O failures statting/chmodding the database file.","triggerScenarios":"Opening a CCR sqlite store whose database file exists but cannot be chmod'd (owned by another user or on a filesystem that does not support chmod, e.g. some network mounts); the file cannot be created/secured because the directory is read-only; secureSQLiteFile's internal Lstat/Chmod call fails for any reason.","commonSituations":"Database file created previously by root and now opened as an unprivileged user; sqlite db placed on an NFS/SMB or FAT mount where chmod fails or is unsupported; read-only container rootfs while trying to create the db.","solutions":["chown the database file (and -wal/-shm sidecars) to the user running the process, or delete them and let Open recreate them","Move the store to a local filesystem that supports POSIX permissions","Check directory write permission for creating/securing new files (ls -ld)","Ensure the filesystem is mounted read-write, not read-only"],"exampleFix":"// before\nsudo chown root /var/lib/ccr/recovery.db\n// after\nsudo chown appuser:appuser /var/lib/ccr/recovery.db*","handlingStrategy":"validation","validationCode":"func canSecure(dbPath string) error {\n    dir := filepath.Dir(dbPath)\n    if fi, err := os.Stat(dir); err != nil || !fi.IsDir() { return err }\n    f, err := os.OpenFile(filepath.Join(dir, \".ccr-write-test\"), os.O_CREATE|os.O_WRONLY, 0o600)\n    if err != nil { return err }\n    f.Close(); return os.Remove(filepath.Join(dir, \".ccr-write-test\"))\n}","typeGuard":null,"tryCatchPattern":"err := ccr.Open(dbPath)\nif err != nil && strings.Contains(err.Error(), \"secure sqlite\") {\n    return fmt.Errorf(\"check ownership/permissions of %s and its sidecars: %w\", dbPath, err)\n}","preventionTips":["Run the engine consistently as the same user","Keep the store on a local POSIX filesystem, not NFS/FAT","Never run once as root against a user-owned store directory"],"tags":["go","sqlite","permissions"],"backgroundTag":"file-write-permission-denied","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"}