JuliusBrussee/caveman · error
sqlite parent has no security descriptor
Error message
sqlite parent %q has no security descriptor
What it means
engine/ccr on Windows requires the SQLite parent directory to expose a security descriptor with a DACL so it can prove other users cannot write there. A nil descriptor means the OS returned no security information at all, which the library treats as unverifiable and fails closed with 'sqlite parent %q has no security descriptor'.
Solutions
- Relocate the database to a standard local NTFS directory (e.g. %LOCALAPPDATA%\myapp) that reports a normal security descriptor
- Verify the path is a real directory on an NTFS volume (fsutil fsinfo volumeinfo <drive>) rather than a network, FAT, or virtual mount
- If a placeholder/virtualization layer strips descriptors, exclude the app data dir from it or run with it disabled
Example fix
// before
store, _ := engine.OpenCCRStore(`Z:\ramdisk\db.sqlite`) // non-NTFS volume, no descriptor
// after
store, _ := engine.OpenCCRStore(filepath.Join(os.Getenv("LOCALAPPDATA"), "myapp", "db.sqlite")) Defensive patterns
Strategy: validation
Validate before calling
// pre-check: directory must report a security descriptor
sd, err := windows.GetNamedSecurityInfo(parentDir, windows.SE_FILE_OBJECT, windows.DACL_SECURITY_INFORMATION)
if err != nil || sd == nil {
return fmt.Errorf("%s does not expose a security descriptor; use a local NTFS dir", parentDir)
} Prevention
- Avoid FAT32/exFAT, RAM disks, and WSL/container-mapped paths for app data
- Use %LOCALAPPDATA% (NTFS) for the database location
- Exclude the data dir from virtualization layers that strip security descriptors
When it happens
Trigger: validateSQLiteParentSecurity receives a nil SECURITY_DESCRIPTOR from GetNamedSecurityInfo despite no error — seen with unusual filesystem objects, non-NTFS volumes, or virtualized/placeholder paths that report no security info.
Common situations: Databases on FAT32/exFAT or RAM disks without ACL support; WSL-mounted or container-mapped paths presenting no Windows security descriptor; paths inside isolated user-profile virtualization layers.
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 grants broad Windows write access
- sqlite parent is group/world writable
- : database parent changed
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/513a4f0b993d9662.
Report an issue: GitHub.
Appendix: source
Thrown at engine/ccr/sqlite_parent_security_windows.go:23
import (
"fmt"
"os"
"unsafe"
"golang.org/x/sys/windows"
)
func validateSQLiteParentSecurity(path string, _ os.FileInfo) error {
descriptor, err := windows.GetNamedSecurityInfo(
path,
windows.SE_FILE_OBJECT,
windows.DACL_SECURITY_INFORMATION,
)
if err != nil {
return fmt.Errorf("inspect sqlite parent ACL %q: %w", path, err)
}
if descriptor == nil {
return fmt.Errorf("sqlite parent %q has no security descriptor", path)
}
dacl, _, err := descriptor.DACL()
if err != nil || dacl == nil {
return fmt.Errorf("sqlite parent %q has no restrictive DACL", path)
}
broad, err := windowsACLGrantsBroadWrite(dacl)
if err != nil {
return fmt.Errorf("inspect sqlite parent ACL %q: %w", path, err)
}
if broad {
return fmt.Errorf("sqlite parent %q grants broad Windows write access", path)
}
return nil
}
func windowsACLGrantsBroadWrite(dacl *windows.ACL) (bool, error) {
broadSIDs := make([]*windows.SID, 0, 4)
for _, sidType := range []windows.WELL_KNOWN_SID_TYPE{View on GitHub (pinned to 3ee70a1026)