matryer/xbar · error

open plugin source

Error message

open plugin source

What it means

loadVariablesFromPluginMetadata (pkg/plugins/variables.go:123-125) wraps os.Open failures on the plugin executable itself (p.Command) with 'open plugin source'. The library reads the plugin's source to parse `<xbar.var>` default variable declarations, so if the executable cannot be opened, defaults cannot be loaded and the error propagates up through loadVariables ('load default vars').

Solutions

  1. Verify the path in p.Command exists and is readable: `ls -l <path>`; reinstall the plugin if missing.
  2. Restore read/execute permissions on the plugin file (chmod +rx).
  3. Update or remove the stale plugin entry so xbar stops referencing the old path.
  4. Refresh the plugin list in xbar (refresh menu) so p.Command is re-resolved from the actual installed location.

Example fix

// before
pluginFile, err := os.Open(p.Command)
if err != nil {
	return nil, errors.Wrap(err, "open plugin source")
}
// after — guard at call site before loading vars
if _, err := os.Stat(p.Command); err != nil {
	return nil, nil // plugin gone; no defaults to load
}
pluginFile, err := os.Open(p.Command)
if err != nil {
	return nil, errors.Wrap(err, "open plugin source")
}
Defensive patterns

Strategy: validation

Validate before calling

info, err := os.Stat(p.Command)
if err != nil {
	log.Printf("plugin source %s missing: %v", p.Command, err)
} else if info.IsDir() {
	log.Printf("plugin source %s is a directory", p.Command)
} else if info.Mode().Perm()&0444 == 0 {
	log.Printf("plugin source %s is not readable", p.Command)
}

Try / catch

if _, err := os.Stat(p.Command); err != nil {
	log.Printf("skipping plugin: source not found: %v", err)
	return nil // do not attempt to run or load vars
}
if err := p.run(); err != nil {
	if strings.Contains(err.Error(), "open plugin source") {
		log.Printf("plugin source unreadable, reinstall the plugin: %+v", err)
	}
}

Prevention

When it happens

Trigger: The plugin binary/script was deleted, renamed, or moved after its variables were configured; p.Command contains a stale absolute path (plugin dir moved, e.g. ~/Library/Application Support/xbar changed); permission denied on the executable; running with a working directory where a relative p.Command no longer resolves.

Common situations: User updates a plugin (file replaced/removed) while xbar still holds the old path; plugin installed from another user's home directory lacks read permission; plugin uninstalled but xbar tries to rebuild env vars from a stale entry.

Related errors


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

Appendix: source

Thrown at pkg/plugins/variables.go:125

		return nil, errors.Wrap(err, "open vars json file")
	}
	defer f.Close()
	b, err := io.ReadAll(io.LimitReader(f, 1_000_000))
	if err != nil {
		return nil, err
	}
	var vars map[string]interface{}
	if err := json.Unmarshal(b, &vars); err != nil {
		return nil, errors.Wrap(err, "json.Unmarshal")
	}
	return vars, nil
}

func (p *Plugin) loadVariablesFromPluginMetadata() (map[string]interface{}, error) {
	// read the plugin metadata for default values
	pluginFile, err := os.Open(p.Command)
	if err != nil {
		return nil, errors.Wrap(err, "open plugin source")
	}
	defer pluginFile.Close()
	pluginFileB, err := io.ReadAll(io.LimitReader(pluginFile, 1_000_000))
	if err != nil {
		return nil, errors.Wrap(err, "read plugin source")
	}
	pluginMetadata, err := metadata.Parse(metadata.DebugFunc(p.Debugf), p.CleanFilename(), string(pluginFileB))
	if err != nil {
		return nil, errors.Wrap(err, "metadata.Parse")
	}
	vars := make(map[string]interface{})
	for _, pluginVar := range pluginMetadata.Vars {
		if pluginVar.Default == "" {
			// skip values with no default
			continue
		}
		vars[pluginVar.Name] = pluginVar.DefaultValue()
	}

View on GitHub (pinned to d624239058)