gastownhall/beads · error
failed to create user config directory: %w
Error message
failed to create user config directory: %w
What it means
SetUserYamlConfig resolves the user config path via UserConfigYamlPath, then ensures the parent directory exists with os.MkdirAll(dir, 0o755). If directory creation fails, the error is wrapped as 'failed to create user config directory'. This happens before any file is created or written.
Source
Thrown at internal/config/yaml_config.go:495
// uses (SetUserYamlConfig, setYamlConfigAtPath, the metrics bootstrap);
// rewriting at 0644 would relax this shared user config to world-readable.
if err := os.WriteFile(configPath, []byte(newContent), 0o600); err != nil { //nolint:gosec // configPath is from UserConfigYamlPath
return fmt.Errorf("failed to write user config.yaml: %w", err)
}
return nil
}
func SetUserYamlConfig(key, value string) error {
if err := validateYamlConfigValue(key, value); err != nil {
return err
}
configPath, err := UserConfigYamlPath()
if err != nil {
return err
}
if err := os.MkdirAll(filepath.Dir(configPath), 0o755); err != nil {
return fmt.Errorf("failed to create user config directory: %w", err)
}
if _, err := os.Stat(configPath); os.IsNotExist(err) {
if err := os.WriteFile(configPath, []byte{}, 0o600); err != nil {
return fmt.Errorf("failed to create user config.yaml: %w", err)
}
} else if err != nil {
return fmt.Errorf("failed to stat user config.yaml: %w", err)
}
return setYamlConfigAtPath(configPath, key, value)
}
func setYamlConfigAtPath(configPath, key, value string) error {
// Normalize key to canonical yaml format
normalizedKey := normalizeYamlKey(key)
// Read existing config
content, err := os.ReadFile(configPath) //nolint:gosec // configPath is from findProjectConfigYamlView on GitHub (pinned to 71377f2769)
Solutions
- Ensure a file named like the config dir doesn't already exist (e.g. ~/.beads as a file) — remove or rename it.
- Fix HOME/USERPROFILE to a writable absolute path and retry.
- Check permissions on the parent (chmod u+w ~) and free disk space if ENOSPC.
- Read the wrapped %w cause (EACCES/ENOTDIR/EROFS) and fix the specific filesystem condition.
Example fix
// before $ ls -la ~ | grep beads -rw-r--r-- 1 user user .beads # a file, not a dir bd config set ... // mkdir fails ENOTDIR // after mv ~/.beads ~/.beads.bak mkdir -p ~/.beads bd config set ...
Defensive patterns
Strategy: validation
Validate before calling
dir := filepath.Join(os.Getenv("HOME"), ".beads")
if fi, err := os.Stat(dir); err == nil && !fi.IsDir() {
return fmt.Errorf("%s exists but is not a directory", dir)
}
if err := unix.Access(filepath.Dir(dir), unix.W_OK); err != nil {
return fmt.Errorf("parent %s not writable: %w", filepath.Dir(dir), err)
} Try / catch
err := config.SetUserYamlConfig(key, value)
if err != nil && strings.Contains(err.Error(), "failed to create user config directory") {
if mkErr := os.MkdirAll(homeDir, 0o755); mkErr != nil {
return fmt.Errorf("unwritable HOME; set HOME to a writable dir: %w", mkErr)
}
return config.SetUserYamlConfig(key, value)
} Prevention
- Point HOME/USERPROFILE at a writable directory in CI and containers.
- Never create a file where the .beads directory belongs.
- Keep home writable: check `touch ~/.probe` when troubleshooting.
- Avoid read-only bind mounts over $HOME in sandboxes.
When it happens
Trigger: os.MkdirAll on the user config directory (e.g. ~/.beads) fails because HOME is unwritable, a non-directory file exists at the target path, or permission/ENOSPC/EROFS conditions apply.
Common situations: HOME pointing to a read-only or nonexistent location; a regular file named .beads blocking directory creation; restricted sandboxed CI runners with read-only HOME.
Related errors
- write metadata.json: %w
- failed to save config: %w
- failed to set beads.role config: %w
- failed to write config.yaml: %w
- refusing to chmod %s: path is not a directory
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/5a1a9a87a2a425f4.
Report an issue: GitHub.