{"record":{"id":"9704ce900ceb0d32","repo":"matryer/xbar","slug":"json-marshalindent","errorCode":null,"errorMessage":"json.MarshalIndent","messagePattern":"json\\.MarshalIndent","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/plugins/variables.go","lineNumber":23,"sourceCode":"\t\"fmt\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"sync\"\n\n\t\"github.com/matryer/xbar/pkg/metadata\"\n\t\"github.com/pkg/errors\"\n)\n\n// variableJSONFileExt is the extension for the variable JSON payload.\nconst variableJSONFileExt = \".vars.json\"\n\n// SaveVariableValues saves the values for a plugin.\nfunc SaveVariableValues(pluginDir, installedPluginPath string, values map[string]interface{}) error {\n\tb, err := json.MarshalIndent(values, \"\", \"\\t\")\n\tif err != nil {\n\t\treturn errors.Wrap(err, \"json.MarshalIndent\")\n\t}\n\tfilename := filepath.Join(pluginDir, installedPluginPath+variableJSONFileExt)\n\terr = ioutil.WriteFile(filename, b, 0666)\n\tif err != nil {\n\t\treturn errors.Wrap(err, \"WriteFile\")\n\t}\n\treturn nil\n}\n\n// LoadVariableValues loads the variables for a plugin.\nfunc LoadVariableValues(pluginDir, installedPluginPath string) (map[string]interface{}, error) {\n\tfilename := filepath.Join(pluginDir, installedPluginPath+variableJSONFileExt)\n\tf, err := os.Open(filename)\n\tif err != nil {\n\t\tif os.IsNotExist(err) {\n\t\t\t// no file - but not an error, just empty map\n\t\t\treturn map[string]interface{}{}, nil\n\t\t}","sourceCodeStart":5,"sourceCodeEnd":41,"githubUrl":"https://github.com/matryer/xbar/blob/d624239058997c80118eaebe2e7f8331b3c765e0/pkg/plugins/variables.go#L5-L41","documentation":"SaveVariableValues serializes the plugin's variable values with json.MarshalIndent before writing <pluginPath>.vars.json. This error wraps any Marshal failure. Because values are map[string]interface{}, marshaling fails only when a value contains an unsupported type (e.g. a channel, func, complex number, or a struct with unexported/invalid fields) or an invalid UTF-8/radical type that json cannot represent.","triggerScenarios":"Calling plugins.SaveVariableValues(pluginDir, installedPluginPath, values) where values contains a non-JSON-serializable value, such as a func, chan, os.File, sync.Mutex embedded in a struct being marshaled, or math.NaN/Inf floats (which produce an error in Go's json on unsupported types; NaN/Inf actually error via 'unsupported value').","commonSituations":"Passing raw Go structures holding handles (DB connections, file objects) instead of plain data; storing time.T without proper handling in older patterns; values captured from reflection-based code; nested maps containing custom types without MarshalJSON.","solutions":["Sanitize values to plain JSON types (string, float64, bool, nil, []interface{}, map[string]interface{}) before calling SaveVariableValues","Check errors.Cause(err) — it names the offending type, e.g. 'json: unsupported type: func()'; find and remove/convert that field","Implement json.Marshaler on custom types you must persist, or convert to a serializable DTO first"],"exampleFix":"// before\nvalues := map[string]interface{}{\"callback\": func() {}}\nerr := plugins.SaveVariableValues(dir, path, values) // json.MarshalIndent error\n// after\nvalues := map[string]interface{}{\"name\": cfg.Name, \"timeout\": cfg.Timeout.Seconds()}\nif err := plugins.SaveVariableValues(dir, path, values); err != nil {\n    return fmt.Errorf(\"save variable values: %w\", err)\n}","handlingStrategy":"validation","validationCode":"func jsonSafe(v interface{}) error {\n    var b []byte\n    var err error\n    if b, err = json.Marshal(v); err != nil {\n        return err\n    }\n    _ = b\n    return nil\n}\n// call: if err := jsonSafe(values); err != nil { /* sanitize values */ }","typeGuard":null,"tryCatchPattern":"if err := plugins.SaveVariableValues(dir, path, values); err != nil {\n    var ute *json.UnsupportedTypeError\n    var uve *json.UnsupportedValueError\n    if errors.As(errors.Cause(err), &ute) {\n        log.Printf(\"cannot serialize type %v\", ute.Type)\n    } else if errors.As(errors.Cause(err), &uve) {\n        log.Printf(\"cannot serialize value %v\", uve.Value)\n    }\n    return err\n}","preventionTips":["Store only plain JSON-shaped data (strings, numbers, bools, slices, maps) in variable values","Implement json.Marshaler on custom types or convert to DTOs before saving","Never put funcs, channels, or file handles in the values map","Dry-run with json.Marshal to catch bad values before they hit the persistence path"],"tags":["go","json","serialization","plugins"],"backgroundTag":"json-unsupported-type","analyzedSha":"d624239058997c80118eaebe2e7f8331b3c765e0","analyzedAt":"2026-09-02T22:38:22.007Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}