JuliusBrussee/caveman · error

secure sqlite sidecar

Error message

secure sqlite sidecar %q: %w

What it means

Besides the main database file, PrepareSQLitePathCanonical also hardens the SQLite WAL (-wal) and shared-memory (-shm) sidecar files with secureSQLiteFile. This error wraps failure securing one of those sidecars (which may not yet exist; securing is attempted without create). It indicates a chmod/stat failure on a sidecar file.

Solutions

  1. Delete the stale -wal and -shm files (with the store closed) and let SQLite recreate them with correct ownership
  2. chown the sidecars to the current user
  3. Align on a single run-as user for the engine to avoid mixed ownership
  4. Verify no other process is concurrently holding the database when the error repeats

Example fix

// before
# root-created sidecars block the app user
$ sudo rm /var/lib/ccr/recovery.db-wal /var/lib/ccr/recovery.db-shm
// after
$ ls -l /var/lib/ccr/ # only recovery.db owned by appuser; Open recreates sidecars
Defensive patterns

Strategy: validation

Validate before calling

for _, suf := range []string{"-wal", "-shm"} {
    fi, err := os.Lstat(dbPath + suf)
    if err == nil && !fi.Mode().IsRegular() {
        return fmt.Errorf("%s%s is not a regular file", dbPath, suf)
    }
}

Try / catch

err := ccr.Open(dbPath)
if err != nil && strings.Contains(err.Error(), "secure sqlite sidecar") {
    os.Remove(dbPath+"-wal"); os.Remove(dbPath+"-shm") // store must be closed
    err = ccr.Open(dbPath)
}

Prevention

When it happens

Trigger: Opening a CCR store after a previous unclean shutdown left -wal/-shm files owned by a different user (commonly root after a sudo run); the sidecar files exist on a filesystem where chmod is not permitted; the sidecar was replaced by something non-regular during the secure pass.

Common situations: Mixed-ownership sidecars after switching between running the engine as root and as a normal user; crash-recovery leftovers in a shared data directory; container user-remapping making previously created sidecars inaccessible.

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/b26e15ca93fa60ae. Report an issue: GitHub.

Appendix: source

Thrown at engine/ccr/store_sqlite.go:312

		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 {
			return err
		}
	}
	return nil

View on GitHub (pinned to 3ee70a1026)