{"record":{"id":"241737725c1adb6a","repo":"matryer/xbar","slug":"writefile","errorCode":null,"errorMessage":"WriteFile","messagePattern":"WriteFile","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/plugins/variables.go","lineNumber":28,"sourceCode":"\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}\n\t\treturn nil, errors.Wrap(err, \"Open\")\n\t}\n\tdefer f.Close()\n\tb, err := io.ReadAll(io.LimitReader(f, 1_000_000 /* ~1MB */))\n\tif err != nil {","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/matryer/xbar/blob/d624239058997c80118eaebe2e7f8331b3c765e0/pkg/plugins/variables.go#L10-L46","documentation":"This error is produced by SaveVariableValues (pkg/plugins/variables.go:26) when ioutil.WriteFile fails to persist the plugin's variable JSON file at <pluginDir>/<installedPluginPath>.vars.json. The underlying OS error (permission denied, missing directory, disk full, path too long) is wrapped with pkg/errors so the message reads 'WriteFile: <os error>'. It is thrown because the library cannot guarantee the plugin variables directory exists or is writable.","triggerScenarios":"Calling SaveVariableValues with a pluginDir that does not exist or is read-only; the directory was deleted while xbar is running; disk quota/full disk; installedPluginPath contains path components whose parent directories do not exist; saving from tests with an invalid temp pluginDir.","commonSituations":"Users install a plugin, edit its variables, and the settings UI fails silently or reports the save error because ~/.local/share/xbar (or the equivalent plugin dir) was moved, synced/restored by backup tools with wrong permissions, or the disk is full. Also seen in CI where the plugin dir is mounted read-only.","solutions":["Create the target directory before saving: os.MkdirAll(filepath.Dir(filename), 0755) prior to calling SaveVariableValues.","Check filesystem permissions on pluginDir and its parents (ls -la) and fix with chmod/chown.","Verify free disk space (df -h) and quota limits.","Validate installedPluginPath has no surprising path separators that point outside pluginDir.","Handle the returned error in the UI so users see the save failed instead of silently losing settings."],"exampleFix":"// before\nerr := plugins.SaveVariableValues(pluginDir, installedPluginPath, values)\nif err != nil { log.Fatal(err) }\n// after\nif err := os.MkdirAll(filepath.Join(pluginDir, filepath.Dir(installedPluginPath)), 0755); err != nil {\n\tlog.Fatal(err)\n}\nif err := plugins.SaveVariableValues(pluginDir, installedPluginPath, values); err != nil {\n\tlog.Fatalf(\"saving plugin variables: %v\", err)\n}","handlingStrategy":"try-catch","validationCode":"filename := filepath.Join(pluginDir, installedPluginPath+\".vars.json\")\nif err := os.MkdirAll(filepath.Dir(filename), 0755); err != nil {\n\treturn fmt.Errorf(\"plugin dir not writable: %w\", err)\n}\nif f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE, 0666); err != nil {\n\treturn fmt.Errorf(\"cannot write %s: %w\", filename, err)\n} else {\n\tf.Close()\n}","typeGuard":null,"tryCatchPattern":"if err := plugins.SaveVariableValues(pluginDir, installedPluginPath, values); err != nil {\n\tvar pe *fs.PathError\n\tif errors.As(err, &pe) && errors.Is(pe.Err, fs.ErrPermission) {\n\t\t// surface a 'check folder permissions' message to the user\n\t}\n\tlog.Printf(\"save variables failed: %+v\", err) // full pkg/errors chain\n}","preventionTips":["Always os.MkdirAll the plugin directory before saving variables.","Never mount the plugin directory read-only if variables are user-editable.","Monitor free disk space on the volume holding the plugin dir.","Test variable saving in CI with a real temp directory (t.TempDir)."],"tags":["go","filesystem","file-write","permissions"],"backgroundTag":"file-write-permission-denied","analyzedSha":"d624239058997c80118eaebe2e7f8331b3c765e0","analyzedAt":"2026-09-02T22:38:22.007Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}