JuliusBrussee/caveman · error

secure sqlite

Error message

secure sqlite %q: %w

What it means

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.

Solutions

  1. chown the database file (and -wal/-shm sidecars) to the user running the process, or delete them and let Open recreate them
  2. Move the store to a local filesystem that supports POSIX permissions
  3. Check directory write permission for creating/securing new files (ls -ld)
  4. Ensure the filesystem is mounted read-write, not read-only

Example fix

// before
sudo chown root /var/lib/ccr/recovery.db
// after
sudo chown appuser:appuser /var/lib/ccr/recovery.db*
Defensive patterns

Strategy: validation

Validate before calling

func canSecure(dbPath string) error {
    dir := filepath.Dir(dbPath)
    if fi, err := os.Stat(dir); err != nil || !fi.IsDir() { return err }
    f, err := os.OpenFile(filepath.Join(dir, ".ccr-write-test"), os.O_CREATE|os.O_WRONLY, 0o600)
    if err != nil { return err }
    f.Close(); return os.Remove(filepath.Join(dir, ".ccr-write-test"))
}

Try / catch

err := ccr.Open(dbPath)
if err != nil && strings.Contains(err.Error(), "secure sqlite") {
    return fmt.Errorf("check ownership/permissions of %s and its sidecars: %w", dbPath, err)
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "Permission denied" / "Failed to write" file errors: why a library can't write its files to disk (EACCES, EPERM, ENOSPC) and how to fix them — this error's family across 43 libraries.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/5aef0877ec5d41ac. Report an issue: GitHub.

Appendix: source

Thrown at engine/ccr/store_sqlite.go:308

		return "", fmt.Errorf("resolve sqlite parent %q: %w", parent, err)
	}
	resolvedParent, err := filepath.EvalSymlinks(absoluteParent)
	if err != nil {
		return "", fmt.Errorf("resolve sqlite parent %q: %w", parent, err)
	}
	parentInfo, err := os.Stat(resolvedParent)
	if err != nil {
		return "", fmt.Errorf("inspect sqlite parent %q: %w", resolvedParent, err)
	}
	if !parentInfo.IsDir() {
		return "", fmt.Errorf("sqlite parent %q must be a directory", resolvedParent)
	}
	if err := validateSQLiteParentSecurity(resolvedParent, parentInfo); err != nil {
		return "", err
	}
	canonicalPath := filepath.Join(resolvedParent, filepath.Base(path))
	if err := secureSQLiteFile(canonicalPath, true); err != nil {
		return "", fmt.Errorf("secure sqlite %q: %w", canonicalPath, err)
	}
	for _, suffix := range []string{"-wal", "-shm"} {
		if err := secureSQLiteFile(canonicalPath+suffix, false); err != nil {
			return "", fmt.Errorf("secure sqlite sidecar %q: %w", canonicalPath+suffix, err)
		}
	}
	return canonicalPath, nil
}

func secureSQLiteFiles(path string) error {
	if path == ":memory:" {
		return nil
	}
	if _, err := inspectSQLiteGeneration(path); err != nil {
		return err
	}
	for _, suffix := range sqliteSuffixes {
		if err := secureSQLiteFile(path+suffix, false); err != nil {

View on GitHub (pinned to 3ee70a1026)