{"record":{"id":"6deac5cab5ac5332","repo":"charmbracelet/crush","slug":"failed-to-unmarshal-provider-data-from-cache-w","errorCode":null,"errorMessage":"failed to unmarshal provider data from cache: %w","messagePattern":"failed to unmarshal provider data from cache: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/config/provider.go","lineNumber":278,"sourceCode":"}\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)\n\t}\n\n\t// Written through a temporary file and renamed into place. Several Crush\n\t// instances start independently and race to refresh this cache, and a","sourceCodeStart":260,"sourceCodeEnd":296,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/config/provider.go#L260-L296","documentation":"Thrown by cache.Get() when the cache file exists but json.Unmarshal fails on its contents. Indicates the cached provider data is not valid JSON or does not match the expected schema (e.g. truncated or hand-edited file).","triggerScenarios":"cache.Get() reads a file that was truncated mid-write (older non-atomic writer), hand-edited with a syntax error, or written by an incompatible Crush version with a changed schema.","commonSituations":"Crash during an old version's cache write; manual editing of the cache file; downgrade to a version expecting an older schema; disk corruption.","solutions":["Delete the cache file and run `crush update-providers` to regenerate it.","Verify the file is valid JSON (e.g. with `jq . <file>`).","Upgrade/downgrade to a matching Crush version if schema drift is the cause.","Use unmarshal options (DisallowUnknownFields off / custom decode) if calling the library directly with newer data."],"exampleFix":"// before (corrupt cache)\ncrush run  // -> failed to unmarshal provider data from cache\n// after\nrm ~/.cache/crush/providers.json && crush update-providers","handlingStrategy":"validation","validationCode":"var probe json.RawMessage\nif err := json.Unmarshal(data, &probe); err != nil { regenerateCache() }","typeGuard":"func isValidProviderCache(path string) bool {\n    data, err := os.ReadFile(path)\n    if err != nil { return false }\n    return json.Unmarshal(data, &map[string]any{}) == nil || json.Valid(data)\n}","tryCatchPattern":"v, _, err := c.Get()\nif err != nil {\n    var unmarshalErr *json.UnmarshalTypeError\n    if errors.As(err, &unmarshalErr) || strings.Contains(err.Error(), \"unmarshal\") {\n        os.Remove(c.path); v = fetchFresh()\n    }\n}","preventionTips":["Never hand-edit cache files","Keep Crush versions consistent so schemas match","Use the atomic write path (Store) to avoid truncated files","Validate cache JSON before consuming in scripts"],"tags":["json","cache","corruption"],"backgroundTag":"json-unmarshal-failed","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}