JuliusBrussee/caveman · error
sqlite parent is group/world writable
Error message
sqlite parent %q is group/world writable
What it means
engine/ccr hardens its SQLite database by requiring the parent directory to prevent other local users from replacing the (0600) database file. On POSIX, a parent directory that is group- or world-writable throws this error unless it is sticky (like /tmp), where the sticky bit already blocks replacement by other users.
Solutions
- Move the database into a private directory owned by the running user, e.g. chmod 700 ~/.local/state/myapp or the app's own data dir
- Fix the parent directory permissions: chmod go-w <parent> (e.g. 0755 or 0700) before starting the store
- If you intentionally use a shared dir like /tmp, ensure it has the sticky bit (chmod +t) or preferably use a per-user directory instead
Example fix
// before
store, _ := engine.OpenCCRStore("/var/tmp/shared/ccr.sqlite") // /var/tmp/shared is 0777
// after
os.Chmod("/var/tmp/shared", 0o700) // or use a per-user data dir
store, _ := engine.OpenCCRStore("/var/tmp/shared/ccr.sqlite") Defensive patterns
Strategy: validation
Validate before calling
info, err := os.Stat(filepath.Dir(path))
if err != nil { return err }
perm := info.Mode().Perm()
if perm&0o022 != 0 && info.Mode()&os.ModeSticky == 0 {
return fmt.Errorf("parent %s must not be group/world writable", filepath.Dir(path))
} Prevention
- Keep app data in a 0700 directory owned by the running user
- Avoid chmod 0777 'fixes' on data directories
- Set a restrictive umask (077) in deployment scripts
- If using shared temp dirs, ensure the sticky bit is set
When it happens
Trigger: Opening/creating a CCR SQLite store whose parent directory has write permission for group or other (perm bits & 0o022 nonzero) without the sticky bit — e.g. a shared /var/tmp-style directory or a data dir chmod'ed 0777 or 0775.
Common situations: Databases placed in shared temp directories lacking the sticky bit; deployment scripts chmod'ing data dirs 0777 'to fix permissions'; containers running with permissive umask; multi-user hosts where the app writes to a group-shared directory.
Understand the failure class
Background: "You do not have permission" / 403 Forbidden errors: authenticated but not allowed — causes and fixes across open-source libraries — this error's family across 31 libraries.
Related errors
- inspect sqlite parent ACL
- sqlite parent has no restrictive DACL
- sqlite parent has no security descriptor
- : database parent changed
- : : inspect database parent
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/535ff223efa397b2.
Report an issue: GitHub.
Appendix: source
Thrown at engine/ccr/sqlite_parent_security_posix.go:15
//go:build !windows && !js
package ccr
import (
"fmt"
"os"
"path/filepath"
)
func validateSQLiteParentSecurity(path string, info os.FileInfo) error {
// Sticky shared temp directories (/tmp, macOS /private/tmp) prevent another
// user from replacing the 0600 file. Other group/world-writable parents do not.
if info.Mode().Perm()&0o022 != 0 && info.Mode()&os.ModeSticky == 0 {
return fmt.Errorf("sqlite parent %q is group/world writable", path)
}
return nil
}
func createSQLiteFile(path string) error {
// Close before publishing: another Store in this process may open the
// database as soon as its name exists. Closing even a creation descriptor
// after that point would discard SQLite's process-wide POSIX locks.
file, err := os.CreateTemp(filepath.Dir(path), ".caveman-sqlite-*")
if err != nil {
return err
}
defer os.Remove(file.Name())
if err := file.Close(); err != nil {
return err
}
// Link is atomic and refuses to replace an existing file or symlink.
return os.Link(file.Name(), path)View on GitHub (pinned to 3ee70a1026)