JuliusBrussee/caveman · error

inspect sqlite parent

Error message

inspect sqlite parent %q: %w

What it means

PrepareSQLitePathCanonical resolves the parent directory of the SQLite database path via filepath.EvalSymlinks, and this error wraps any failure from that resolution. It means the library could not canonicalize the parent directory (e.g. it does not exist, is inaccessible, or a symlink loop was found) before opening the CCR SQLite store, so it refuses to proceed rather than opening an unsafe path.

Solutions

  1. Verify the parent directory of the sqlite path exists; create it with os.MkdirAll before calling Open
  2. Check for broken symlinks in the path (ls -l / readlink -f) and repair or remove them
  3. Ensure the process has execute/traverse permission on every component of the parent path
  4. Fix the configured database path in your engine/store configuration
  5. Run as a user with access to the directory; on Windows confirm the path is a valid NTFS path without a symlink loop

Example fix

// before
store, err := ccr.Open("/var/lib/ccr/cache/recovery.db") // parent missing
// after
if err := os.MkdirAll("/var/lib/ccr/cache", 0o700); err != nil { log.Fatal(err) }
store, err := ccr.Open("/var/lib/ccr/cache/recovery.db")
Defensive patterns

Strategy: validation

Validate before calling

func parentExists(dbPath string) error {
    parent := filepath.Dir(dbPath)
    if _, err := filepath.EvalSymlinks(parent); err != nil {
        return fmt.Errorf("cannot resolve parent %q: %w", parent, err)
    }
    return nil
}

Try / catch

path, err := ccr.PrepareSQLitePath(dbPath)
if err != nil {
    var pe *fs.PathError
    if errors.As(err, &pe) { os.MkdirAll(filepath.Dir(dbPath), 0o700) }
    return err
}

Prevention

When it happens

Trigger: Calling ccr.Open/PrepareSQLitePath with a database path whose parent directory does not exist on disk; the parent path contains a broken symlink; the parent symlink chain forms a loop; or the process lacks permission to traverse a symlinked parent on any platform including Windows.

Common situations: A config file points at a data directory that was deleted or never created (e.g. ~/.caveman/ccr removed by a cleanup tool); a docker volume or mount point not yet mounted when the engine starts; running on Windows where EvalSymlinks fails for certain paths; typo in the directory portion of the configured db path.

Related errors


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

Appendix: source

Thrown at engine/ccr/store_sqlite.go:298

func PrepareSQLitePathCanonical(path string) (string, error) {
	if path == ":memory:" {
		return path, nil
	}
	if strings.TrimSpace(path) == "" {
		return "", fmt.Errorf("ccr sqlite path is required")
	}
	parent := filepath.Dir(path)
	absoluteParent, err := filepath.Abs(parent)
	if err != nil {
		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
}

View on GitHub (pinned to 3ee70a1026)