charmbracelet/crush · error
failed to read file: %w
Error message
failed to read file: %w
What it means
When UpdateProviders' pathOrURL is neither "embedded" nor an http(s) URL, it is treated as a filesystem path and read with os.ReadFile. This error wraps the read failure (file not found, permission denied, is-a-directory). It indicates the local provider JSON source could not be opened.
Source
Thrown at internal/config/provider.go:74
// UpdateProviders updates the Catwalk providers list from a specified source.
func UpdateProviders(pathOrURL string) error {
var providers []catwalk.Provider
pathOrURL = cmp.Or(pathOrURL, os.Getenv("CATWALK_URL"), defaultCatwalkURL)
switch {
case pathOrURL == "embedded":
providers = embedded.GetAll()
case strings.HasPrefix(pathOrURL, "http://") || strings.HasPrefix(pathOrURL, "https://"):
var err error
providers, err = catwalk.NewWithURL(pathOrURL).GetProviders(context.Background(), "")
if err != nil {
return fmt.Errorf("failed to fetch providers from Catwalk: %w", err)
}
default:
content, err := os.ReadFile(pathOrURL)
if err != nil {
return fmt.Errorf("failed to read file: %w", err)
}
if err := json.Unmarshal(content, &providers); err != nil {
return fmt.Errorf("failed to unmarshal provider data: %w", err)
}
if len(providers) == 0 {
return fmt.Errorf("no providers found in the provided source")
}
}
if err := newCache[[]catwalk.Provider](cachePathFor("providers")).Store(providers); err != nil {
return fmt.Errorf("failed to save providers to cache: %w", err)
}
slog.Info("Providers updated successfully", "count", len(providers), "from", pathOrURL, "to", cachePathFor)
return nil
}
// resolveHyperAPIKey returns the Hyper API key from the environment orView on GitHub (pinned to 7944b8e522)
Solutions
- Verify the path exists: `ls -l <path>` and fix typos or pass an absolute path.
- Expand `~` yourself before calling (os.UserHomeDir or filepath.Join).
- Fix permissions with chmod/chown so the process user can read the file.
- If it should be remote, prefix the URL with http:// or https:// so it is not treated as a file path.
Example fix
// before
err := config.UpdateProviders("~/providers.json")
// after
home, _ := os.UserHomeDir()
err := config.UpdateProviders(filepath.Join(home, "providers.json")) Defensive patterns
Strategy: validation
Validate before calling
path := src
if strings.HasPrefix(path, "~") {
home, _ := os.UserHomeDir()
path = filepath.Join(home, path[1:])
}
if fi, err := os.Stat(path); err != nil {
return fmt.Errorf("provider source not accessible: %w", err)
} else if fi.IsDir() {
return fmt.Errorf("provider source is a directory: %s", path)
} Try / catch
if _, err := os.Stat(path); err != nil {
if errors.Is(err, os.ErrNotExist) {
slog.Error("provider file missing", "path", path)
} else if errors.Is(err, os.ErrPermission) {
slog.Error("provider file unreadable", "path", path)
}
return err
} Prevention
- Use absolute paths in config, never bare `~`.
- Verify the file exists from the same working directory the app runs in.
- Keep provider JSON files 0o644 and owned by the running user.
- Check for http(s) prefix confusion: file paths must not start with http.
When it happens
Trigger: Calling config.UpdateProviders with a local path (e.g. "providers.json", "~/catwalk.json") that does not exist, has wrong permissions, is a directory, or contains an unexpanded `~` which Go does not expand.
Common situations: Typo in the path; running from a different working directory with a relative path; forgetting that `~` is not shell-expanded before reaching os.ReadFile; file created by another user with 0600 perms.
Understand the failure class
Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.
Related errors
- no providers found
- no providers found matching %q
- no providers configured - please run 'crush' to set up a pro
- failed to load projects: %w
- invalid source %q, must be 'catwalk' or 'hyper'
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/69935961f067b734.
Report an issue: GitHub.