charmbracelet/crush · error
failed to create data directory: %q %w
Error message
failed to create data directory: %q %w
What it means
createDotCrushDir creates the workspace data directory (mode 0700) and this error wraps os.MkdirAll failure. The quoted %q shows exactly which directory path could not be created, alongside the underlying OS error. It aborts workspace creation because subsequent data (DB, config) cannot live without the directory.
Source
Thrown at internal/backend/util.go:11
package backend
import (
"fmt"
"os"
"path/filepath"
)
func createDotCrushDir(dir string) error {
if err := os.MkdirAll(dir, 0o700); err != nil {
return fmt.Errorf("failed to create data directory: %q %w", dir, err)
}
gitIgnorePath := filepath.Join(dir, ".gitignore")
if _, err := os.Stat(gitIgnorePath); os.IsNotExist(err) {
if err := os.WriteFile(gitIgnorePath, []byte("*\n"), 0o644); err != nil {
return fmt.Errorf("failed to create .gitignore file: %q %w", gitIgnorePath, err)
}
}
return nil
}
View on GitHub (pinned to 7944b8e522)
Solutions
- Read the quoted path and underlying OS error to identify permission vs existence issues.
- If a regular file occupies the path, remove or rename it and retry.
- Check write permission on the parent directory (ls -ld on each component).
- Choose a different data directory on a writable filesystem.
Example fix
// before
// crash on read-only HOME
createDotCrushDir(filepath.Join(os.Getenv("HOME"), ".crush"))
// after
// pick a writable location explicitly
dir := os.Getenv("CRUSH_DATA_DIR")
if dir == "" {
dir = filepath.Join(os.TempDir(), "crush-data")
}
err := createDotCrushDir(dir) Defensive patterns
Strategy: validation
Validate before calling
if fi, err := os.Stat(dir); err == nil {
if !fi.IsDir() {
return fmt.Errorf("%s exists and is not a directory", dir)
}
}
parent := filepath.Dir(dir)
if fi, err := os.Stat(parent); err != nil || !fi.IsDir() {
return fmt.Errorf("parent %s missing", parent)
}
if err := unix.Access(parent, unix.W_OK); err != nil {
return fmt.Errorf("parent %s not writable: %w", parent, err)
} Type guard
func IsMkdirErr(err error) (string, bool) {
var pe *fs.PathError
if errors.As(err, &pe) && errors.Is(pe.Err, fs.ErrExist) || errors.Is(err, fs.ErrPermission) {
return pe.Path, true
}
return "", false
} Try / catch
if err := createDotCrushDir(dir); err != nil {
var pe *fs.PathError
if errors.As(err, &pe) && errors.Is(pe.Err, fs.ErrPermission) {
// fall back to a writable data dir
return createDotCrushDir(filepath.Join(os.TempDir(), "crush"))
}
return err
} Prevention
- Verify the target path is not an existing regular file before creating.
- Run the process as a user with write access to the chosen data location.
- Avoid read-only mounts (containers, CI) for data directories; use a volume or env override.
- Set HOME/CRUSH data dir explicitly in sandboxed environments.
When it happens
Trigger: Calling CreateWorkspace (indirectly via createDotCrushDir) when os.MkdirAll(dir, 0o700) fails — parent path doesn't exist and can't be created, permission denied on a parent, the path exists as a regular file, or the filesystem is read-only.
Common situations: Pointing the data dir at a read-only mount (containers, CI sandboxes); a file named .crush already exists where a directory is expected; running as a user without write access to the parent directory; HOME unset or mispointed.
Related errors
- error creating directory: %w
- failed to create parent directories: %w
- failed to create parent directories: %w
- error checking file: %w
- error writing file: %w
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/d243dd32b235593d.
Report an issue: GitHub.