JuliusBrussee/caveman · error

%w: %w: %v

Error message

%w: %w: %v

What it means

After a successful os.Stat on the database parent, inspectSQLiteGeneration calls validateSQLiteParentSecurity to confirm the directory's permissions are safe. If that validation fails (mode loosened or otherwise insecure), the error is wrapped as ErrStorageChanged plus errStorageUnverifiable: identity could not be verified under the required security posture. As with other unverifiable failures, checkGeneration fails the current operation without latching terminal quarantine.

Solutions

  1. Set the database parent directory to a secure mode, typically 0700 for the owning user: chmod 700 <dir>.
  2. Ensure the directory is owned by the user running the engine (chown).
  3. Move the store out of shared world-writable locations like /tmp or /var/tmp into the user's home.
  4. If a tool keeps resetting the mode, exclude this directory from it or set the store path elsewhere via configuration.
  5. Retry the operation after fixing: this unverifiable error is not latched as terminal quarantine.

Example fix

// before
os.MkdirAll(dataDir, 0o777)
// after
os.MkdirAll(dataDir, 0o700)
Defensive patterns

Strategy: validation

Validate before calling

info, err := os.Stat(filepath.Dir(dbPath))
if err == nil && info.Mode().Perm()&0o077 != 0 {
    return fmt.Errorf("store dir %s too open: %v", filepath.Dir(dbPath), info.Mode().Perm())
}

Prevention

When it happens

Trigger: validateSQLiteParentSecurity rejects the parent directory's mode/ownership — typically because the directory became world- or group-writable (loose mode) between open and this operation, or was created with insecure permissions.

Common situations: A backup/restore tool resets directory modes to 0777; umask misconfiguration created ~/.caveman as 0777; a shared tmp directory was configured as the store location; security scanners 'fixed' permissions to something the validator also rejects.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


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

Appendix: source

Thrown at engine/ccr/store_generation.go:56

		return files, nil
	}
	parent := filepath.Dir(path)
	resolved, err := filepath.EvalSymlinks(parent)
	if err != nil && !errors.Is(err, os.ErrNotExist) {
		return files, fmt.Errorf("%w: %w: inspect database parent: %v", ErrStorageChanged, errStorageUnverifiable, err)
	}
	if err != nil || resolved != parent {
		return files, fmt.Errorf("%w: database parent changed", ErrStorageChanged)
	}
	info, err := os.Stat(parent)
	if errors.Is(err, os.ErrNotExist) {
		return files, fmt.Errorf("%w: database parent changed", ErrStorageChanged)
	}
	if err != nil {
		return files, fmt.Errorf("%w: %w: inspect database parent: %v", ErrStorageChanged, errStorageUnverifiable, err)
	}
	if err := validateSQLiteParentSecurity(parent, info); err != nil {
		return files, fmt.Errorf("%w: %w: %v", ErrStorageChanged, errStorageUnverifiable, err)
	}
	for i, suffix := range sqliteSuffixes {
		info, err := inspectSQLiteFile(path + suffix)
		if errors.Is(err, os.ErrNotExist) && i != 0 {
			continue
		}
		if errors.Is(err, os.ErrNotExist) {
			return files, fmt.Errorf("%w: inspect database%s: %v", ErrStorageChanged, suffix, err)
		}
		if err != nil {
			return files, fmt.Errorf("%w: %w: inspect database%s: %v", ErrStorageChanged, errStorageUnverifiable, suffix, err)
		}
		if !info.Mode().IsRegular() {
			return files, fmt.Errorf("%w: refusing non-regular database%s", ErrStorageChanged, suffix)
		}
		files[i] = info
	}
	return files, nil

View on GitHub (pinned to 3ee70a1026)