matryer/xbar · error
WriteFile
Error message
WriteFile
What it means
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.
Source
Thrown at pkg/plugins/variables.go:28
"sync"
"github.com/matryer/xbar/pkg/metadata"
"github.com/pkg/errors"
)
// variableJSONFileExt is the extension for the variable JSON payload.
const variableJSONFileExt = ".vars.json"
// SaveVariableValues saves the values for a plugin.
func SaveVariableValues(pluginDir, installedPluginPath string, values map[string]interface{}) error {
b, err := json.MarshalIndent(values, "", "\t")
if err != nil {
return errors.Wrap(err, "json.MarshalIndent")
}
filename := filepath.Join(pluginDir, installedPluginPath+variableJSONFileExt)
err = ioutil.WriteFile(filename, b, 0666)
if err != nil {
return errors.Wrap(err, "WriteFile")
}
return nil
}
// LoadVariableValues loads the variables for a plugin.
func LoadVariableValues(pluginDir, installedPluginPath string) (map[string]interface{}, error) {
filename := filepath.Join(pluginDir, installedPluginPath+variableJSONFileExt)
f, err := os.Open(filename)
if err != nil {
if os.IsNotExist(err) {
// no file - but not an error, just empty map
return map[string]interface{}{}, nil
}
return nil, errors.Wrap(err, "Open")
}
defer f.Close()
b, err := io.ReadAll(io.LimitReader(f, 1_000_000 /* ~1MB */))
if err != nil {View on GitHub (pinned to d624239058)
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.
Example fix
// before
err := plugins.SaveVariableValues(pluginDir, installedPluginPath, values)
if err != nil { log.Fatal(err) }
// after
if err := os.MkdirAll(filepath.Join(pluginDir, filepath.Dir(installedPluginPath)), 0755); err != nil {
log.Fatal(err)
}
if err := plugins.SaveVariableValues(pluginDir, installedPluginPath, values); err != nil {
log.Fatalf("saving plugin variables: %v", err)
} Defensive patterns
Strategy: try-catch
Validate before calling
filename := filepath.Join(pluginDir, installedPluginPath+".vars.json")
if err := os.MkdirAll(filepath.Dir(filename), 0755); err != nil {
return fmt.Errorf("plugin dir not writable: %w", err)
}
if f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE, 0666); err != nil {
return fmt.Errorf("cannot write %s: %w", filename, err)
} else {
f.Close()
} Try / catch
if err := plugins.SaveVariableValues(pluginDir, installedPluginPath, values); err != nil {
var pe *fs.PathError
if errors.As(err, &pe) && errors.Is(pe.Err, fs.ErrPermission) {
// surface a 'check folder permissions' message to the user
}
log.Printf("save variables failed: %+v", err) // full pkg/errors chain
} Prevention
- 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).
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "Permission denied" / "Failed to write" file errors: why a library can't write its files to disk (EACCES, EPERM, ENOSPC) and how to fix them — this error's family across 43 libraries.
Related errors
- create directory %s for plugin
- create plugin file %s
- set executable permission on plugin entry point
- ReadDir
- Open
AI-assisted analysis of matryer/xbar@d624239058 (2026-09-02).
Data as JSON: /api/errors/241737725c1adb6a.
Report an issue: GitHub.