{"record":{"id":"3f685d1d26d3de25","repo":"charmbracelet/crush","slug":"failed-to-marshal-provider-data-w","errorCode":null,"errorMessage":"failed to marshal provider data: %w","messagePattern":"failed to marshal provider data: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/config/provider.go","lineNumber":292,"sourceCode":"\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\n\t// truncating write would let one of them read a half-written catalog and\n\t// silently fall back to the bundled copy.\n\tif err := atomicWriteFile(c.path, data, 0o644); err != nil {\n\t\treturn fmt.Errorf(\"failed to write provider data to cache: %w\", err)\n\t}\n\treturn nil\n}\n","sourceCodeStart":274,"sourceCodeEnd":304,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/config/provider.go#L274-L304","documentation":"Thrown by cache.Store() when json.Marshal fails to serialize the provider data being cached. Rare in practice because the value is a well-typed struct, but can occur with values containing unsupported types (channels, funcs, cyclic references) when the generic cache[T] is used with a non-marshalable T.","triggerScenarios":"cache.Store(v) is called with a T that json.Marshal cannot encode: unsupported field types, cycles, or invalid values (NaN) injected into the provider data by a caller.","commonSituations":"Custom builds/extensions feeding non-serializable data into the generic cache; middlewares mutating provider structs with extra fields of unsupported types.","solutions":["Inspect the value being stored for unsupported JSON types (chan, func, cycles).","Fix the producer of the data to only include JSON-serializable fields.","Add json:\"-\" tags to non-serializable fields in the type parameter T.","Wrap Marshal yourself with a custom codec if you need special serialization."],"exampleFix":"// before\ntype P struct { Conn *net.Conn }\n// after\ntype P struct { Conn *net.Conn `json:\"-\"` }","handlingStrategy":"validation","validationCode":"if err := json.Marshal(v); err != nil {\n    // value not serializable; fix before Store\n}","typeGuard":"func marshalable[T any](v T) bool {\n    _, err := json.Marshal(v)\n    return err == nil\n}","tryCatchPattern":"if err := cache.Store(v); err != nil {\n    if strings.Contains(err.Error(), \"marshal\") {\n        log.Warn(\"Skipping cache: value not serializable\")\n    }\n}","preventionTips":["Keep cache type T JSON-safe (no chan/func/cyclic fields)","Tag internal fields with json:\"-\"","Sanitize provider structs before caching"],"tags":["json","serialization","cache"],"backgroundTag":"json-marshal-failed","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}