charmbracelet/crush · error
read config file: %w
Error message
read config file: %w
What it means
atomicWrite reads the current config file before applying the transform callback. Missing files are treated as empty config ({}), but any other read error — permission denied, path is a directory, I/O error — is wrapped as this error and aborts the write.
Source
Thrown at internal/config/store.go:316
// new contents. fn must be pure — no I/O, no network calls.
func (s *ConfigStore) atomicWrite(scope Scope, fn func(current []byte) ([]byte, error)) error {
unlock, err := s.lockConfig(scope)
if err != nil {
return err
}
defer unlock()
path, err := s.configPath(scope)
if err != nil {
return err
}
data, err := os.ReadFile(path)
if err != nil {
if os.IsNotExist(err) {
data = []byte("{}")
} else {
return fmt.Errorf("read config file: %w", err)
}
}
newData, err := fn(data)
if err != nil {
return err
}
return atomicWriteFile(path, newData, 0o600)
}
// configPath returns the file path for the given scope.
func (s *ConfigStore) configPath(scope Scope) (string, error) {
switch scope {
case ScopeWorkspace:
if s.workspacePath == "" {
return "", ErrNoWorkspaceConfig
}View on GitHub (pinned to 7944b8e522)
Solutions
- Fix the file's permissions so the current user can read it (chmod u+r)
- Check whether the config path is a directory (ls -la) and remove/rename it
- Verify ownership matches the running user (chown)
- Restore the config file from backup or delete it so it is recreated as {}
Example fix
// before
// file exists but unreadable:
os.WriteFile(cfgPath, data, 0o000)
store.RemoveConfigField(scope, key) // fails
// after
if info, err := os.Stat(cfgPath); err == nil && info.Mode().Perm()&0o400 == 0 {
os.Chmod(cfgPath, 0o600) // ensure owner-readable before writing
}
store.RemoveConfigField(scope, key) Defensive patterns
Strategy: validation
Validate before calling
info, err := os.Stat(configPath)
if err == nil {
if info.IsDir() {
return fmt.Errorf("%s is a directory, expected a file", configPath)
}
if f, err := os.Open(configPath); err != nil {
return fmt.Errorf("config file unreadable: %w", err)
} else { f.Close() }
} Type guard
func configReadable(path string) bool {
f, err := os.Open(path)
if err != nil { return false }
f.Close()
return true
} Try / catch
var perr *fs.PathError
if errors.As(err, &perr) && errors.Is(perr, fs.ErrPermission) {
os.Chmod(perr.Path, 0o600)
// retry the operation once
} Prevention
- Keep config file ownership aligned with the running user (avoid sudo-created files)
- Do not create directories at paths that should be config files
- Restore or delete unreadable/corrupted config files so {} regeneration kicks in
- Check mode bits (0o600) when provisioning config files via scripts
When it happens
Trigger: Calling writeConfigFields or RemoveConfigField when the config file exists but cannot be read: the file's mode denies read access, the path is actually a directory, or a low-level I/O error occurs. (Not triggered when the file is simply absent.)
Common situations: Config file created by another user/root with restrictive modes; a directory accidentally created at the config file's path; corrupted filesystem; running under a different user via sudo so ownership no longer matches.
Related errors
- failed to create parent directories: %w
- failed to create output file: %w
- failed to access file: %w
- failed to create parent directories: %w
- session ID is required for accessing directories outside wor
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/b917b63572327d3e.
Report an issue: GitHub.