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

  1. Create the target directory before saving: os.MkdirAll(filepath.Dir(filename), 0755) prior to calling SaveVariableValues.
  2. Check filesystem permissions on pluginDir and its parents (ls -la) and fix with chmod/chown.
  3. Verify free disk space (df -h) and quota limits.
  4. Validate installedPluginPath has no surprising path separators that point outside pluginDir.
  5. 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

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


AI-assisted analysis of matryer/xbar@d624239058 (2026-09-02). Data as JSON: /api/errors/241737725c1adb6a. Report an issue: GitHub.