charmbracelet/crush · critical

failed to create data directory: %q %w

Error message

failed to create data directory: %q %w

What it means

setupLocalWorkspace in internal/cmd/root.go creates the data directory (crush's state dir) with os.MkdirAll 0o700. If the OS refuses (bad path, permission denied, parent not writable, path is a file), it returns 'failed to create data directory: %q %w'. Startup aborts since crush cannot persist sessions/state.

Source

Thrown at internal/cmd/root.go:292

	dataDir, _ := cmd.Flags().GetString("data-dir")
	ctx := cmd.Context()

	cwd, err := ResolveCwd(cmd)
	if err != nil {
		return nil, nil, err
	}

	store, err := config.Init(cwd, dataDir, debug)
	if err != nil {
		return nil, nil, err
	}

	cfg := store.Config()
	store.Overrides().SkipPermissionRequests = yolo
	store.Overrides().EnabledChannels = channels

	if err := os.MkdirAll(cfg.Options.DataDirectory, 0o700); err != nil {
		return nil, nil, fmt.Errorf("failed to create data directory: %q %w", cfg.Options.DataDirectory, err)
	}

	gitIgnorePath := filepath.Join(cfg.Options.DataDirectory, ".gitignore")
	if _, err := os.Stat(gitIgnorePath); os.IsNotExist(err) {
		if err := os.WriteFile(gitIgnorePath, []byte("*\n"), 0o644); err != nil {
			return nil, nil, fmt.Errorf("failed to create .gitignore file: %q %w", gitIgnorePath, err)
		}
	}

	if err := projects.Register(cwd, cfg.Options.DataDirectory); err != nil {
		slog.Warn("Failed to register project", "error", err)
	}

	conn, err := db.Connect(ctx, cfg.Options.DataDirectory)
	if err != nil {
		return nil, nil, err
	}

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Check the printed path: ensure no file exists there (rm/mv it if so) and its parent is writable
  2. Fix permissions: mkdir -p and chown/chmod the parent directory
  3. Set a writable data directory in crush config (options/data_directory) e.g. under $HOME
  4. Run outside read-only sandboxes or mount a writable volume for the data dir

Example fix

// before
cfg.Options.DataDirectory = "/var/lib/crush" // root-owned, not writable
// after
cfg.Options.DataDirectory = filepath.Join(os.Getenv("HOME"), ".local", "share", "crush")
Defensive patterns

Strategy: validation

Validate before calling

dir := cfg.Options.DataDirectory
if fi, err := os.Stat(dir); err == nil && !fi.IsDir() {
    return fmt.Errorf("data directory path %s is a file", dir)
}
if err := os.MkdirAll(dir, 0o700); err != nil {
    return fmt.Errorf("cannot create %s: %w", dir, err)
}

Try / catch

if err := os.MkdirAll(dataDir, 0o700); err != nil {
    var pe *fs.PathError
    if errors.As(err, &pe) && errors.Is(pe.Err, syscall.EACCES) {
        // fix permissions or pick another dir
    }
    return err
}

Prevention

When it happens

Trigger: DataDirectory configured to a path whose parent is not writable; a regular file already exists at the path; read-only filesystem; restricted sandbox/containers blocking mkdir; invalid characters or empty path.

Common situations: Running crush in a read-only container or CI sandbox; HOME unset so path resolves oddly; overriding data directory in config to a root-owned location; disk full or quota exceeded.

Related errors


AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29). Data as JSON: /api/errors/d5170763c72d0aea. Report an issue: GitHub.