{"record":{"id":"62986233c73e71aa","repo":"charmbracelet/crush","slug":"failed-to-read-provider-cache-file-w","errorCode":null,"errorMessage":"failed to read provider cache file: %w","messagePattern":"failed to read provider cache file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/config/provider.go","lineNumber":274,"sourceCode":"\t\t}\n\t}\n\t// Provider not found in list; prepend it.\n\tproviderList = append([]catwalk.Provider{provider}, providerList...)\n}\n\ntype cache[T any] struct {\n\tpath string\n}\n\nfunc newCache[T any](path string) cache[T] {\n\treturn cache[T]{path: path}\n}\n\nfunc (c cache[T]) Get() (T, string, error) {\n\tvar v T\n\tdata, err := os.ReadFile(c.path)\n\tif err != nil {\n\t\treturn v, \"\", fmt.Errorf(\"failed to read provider cache file: %w\", err)\n\t}\n\n\tif err := json.Unmarshal(data, &v); err != nil {\n\t\treturn v, \"\", fmt.Errorf(\"failed to unmarshal provider data from cache: %w\", err)\n\t}\n\n\treturn v, etag.Of(data), nil\n}\n\nfunc (c cache[T]) Store(v T) error {\n\tslog.Info(\"Saving provider data to disk\", \"path\", c.path)\n\tif err := os.MkdirAll(filepath.Dir(c.path), 0o755); err != nil {\n\t\treturn fmt.Errorf(\"failed to create directory for provider cache: %w\", err)\n\t}\n\n\tdata, err := json.Marshal(v)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to marshal provider data: %w\", err)","sourceCodeStart":256,"sourceCodeEnd":292,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/config/provider.go#L256-L292","documentation":"Thrown by cache.Get() when os.ReadFile cannot read the provider cache file at c.path. The cache layer treats a missing/corrupt cache as fatal for that read, so callers (e.g. resolveHyperAPIKey) receive the wrapped OS error. Typically means the file does not exist yet or is unreadable.","triggerScenarios":"Calling cache.Get() (directly or via resolveHyperAPIKey / the syncer's anonymous getter) before the cache file was ever written; the file was deleted; wrong permissions; c.path points to a nonexistent directory.","commonSituations":"First run before any cache store; user or cleanup tool removed ~/.cache/crush files; permission restrictions after switching users; cache path misconfigured via env (XDG dirs).","solutions":["Ensure the cache file exists by triggering a refresh: run `crush update-providers` (and `crush update-hyper` if applicable).","Check file permissions on the cache path (and parent dirs) so the current user can read it.","If the cache is corrupt/undesired, delete it and let Crush recreate it.","Verify env vars controlling the cache directory (e.g. XDG_CACHE_HOME) point to a valid, writable location.","Wrap the call to tolerate os.IsNotExist and fall back to fetching the catalog."],"exampleFix":"// before\nv, _, err := cache.Get()\n// after\nv, _, err := cache.Get()\nif errors.Is(err, fs.ErrNotExist) {\n    err = cache.Store(fresh) // or fetch from network\n}","handlingStrategy":"fallback","validationCode":"if _, err := os.Stat(cachePath); errors.Is(err, fs.ErrNotExist) {\n    // skip cache read; fetch fresh data\n}","typeGuard":"func cacheReadable(path string) bool {\n    f, err := os.Open(path)\n    if err != nil { return false }\n    f.Close()\n    return true\n}","tryCatchPattern":"v, etag, err := c.Get()\nif err != nil {\n    if errors.Is(err, fs.ErrNotExist) { v = fetchFresh() } else { return err }\n}","preventionTips":["Always Store() before relying on Get() results","Don't delete cache files under an active Crush install","Verify XDG/HOME env vars point to writable, stable locations","Treat missing cache as cold-start, not a hard failure"],"tags":["filesystem","cache","providers"],"backgroundTag":"file-not-found","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}