JuliusBrussee/caveman · error

create recovery directory

Error message

create recovery directory %q: %w

What it means

openRecoveryStore could not create the directory that will hold the CCR recovery database (ccr.db). The wrapped os.MkdirAll error names the path and underlying OS reason (permissions, read-only fs, etc.). Without this directory the recovery store cannot be opened.

Solutions

  1. Check the wrapped error's path and OS reason; verify permissions on the target directory
  2. If ~/.caveman exists as a file, remove or rename it so it can be a directory
  3. Ensure HOME (or the override env) points to a writable location, or run with adequate privileges
  4. Mount/write to a writable volume and set the home override accordingly

Example fix

// before: HOME=/nonexistent-writable
// after
export HOME=/tmp/caveman-home && caveman-mcp
Defensive patterns

Strategy: validation

Validate before calling

home := os.Getenv("HOME")
if info, err := os.Stat(filepath.Join(home, ".caveman")); err == nil && !info.IsDir() {
  // fix path before launching
}
if err := os.MkdirAll(filepath.Join(home, ".caveman"), 0o700); err != nil { /* fix perms */ }

Try / catch

store, err := openRecoveryStore()
if err != nil {
  var pe *fs.PathError
  if errors.As(err, &pe) { log.Printf("fs problem at %s: %v", pe.Path, pe.Err) }
  os.Exit(1)
}

Prevention

When it happens

Trigger: os.MkdirAll fails on the resolved ~/.caveman (or fallback home) directory because of permissions, a read-only filesystem, or the path existing as a non-directory file.

Common situations: HOME set to an unwritable path in containers/cron; ~/.caveman exists as a regular file; sandboxed environments blocking writes to home.

Understand the failure class

Background: mkdir permission denied (EACCES): failed to create directory errors explained — this error's family across 32 libraries.

Related errors


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

Appendix: source

Thrown at mcp/cmd/caveman-mcp/main.go:86

// openRecoveryStore opens the shared file CCR store the proxy uses, so handles are
// resolvable across processes. CAVEMAN_MCP_EPHEMERAL=1 forces an in-memory store
// (no disk). The path mirrors the proxy: CAVEMAN_CCR_DB, else ~/.caveman/ccr.db.
func openRecoveryStore() (*ccr.Store, error) {
	if os.Getenv("CAVEMAN_MCP_EPHEMERAL") == "1" {
		return ccr.OpenMemory()
	}
	path := os.Getenv("CAVEMAN_CCR_DB")
	if path == "" {
		home := os.Getenv("CAVEMAN_HOME")
		if home == "" {
			h, err := os.UserHomeDir()
			if err != nil {
				return ccr.OpenMemory()
			}
			home = filepath.Join(h, ".caveman")
		}
		if err := os.MkdirAll(home, 0o700); err != nil {
			return nil, fmt.Errorf("create recovery directory %q: %w", home, err)
		}
		path = filepath.Join(home, "ccr.db")
	}
	return ccr.Open(path)
}

View on GitHub (pinned to 3ee70a1026)