matryer/xbar · error

only one plugin file supported: found %d.

Error message

only one plugin file supported: found %d.

What it means

writePluginFiles supports installing exactly one file per plugin. If the plugin metadata declares more than one file, it returns errors.Errorf("only one plugin file supported: found %d.", len(plugin.Files)).

Source

Thrown at pkg/plugins/install.go:111

		candidateBaseName := fmt.Sprintf("%03d-%s", count, plugin.Filename)
		candidatePath = filepath.Join(i.PluginDir, candidateBaseName)
		_, err = os.Stat(candidatePath)
	}
	if !os.IsNotExist(err) {
		return "", err
	}
	return candidatePath, nil
}

// writePluginFiles iterates through the files defined for the plugin, and
// writes them to the plugin installation directory, and sets the entry point
// to be executable.
func (i Installer) writePluginFiles(dstPath string, plugin metadata.Plugin) error {
	if len(plugin.Files) == 0 {
		return errors.New("no plugin files")
	}
	if len(plugin.Files) > 1 {
		return errors.Errorf("only one plugin file supported: found %d.", len(plugin.Files))
	}
	for _, f := range plugin.Files {
		pluginFile := dstPath
		dir := path.Dir(pluginFile)
		if err := os.MkdirAll(dir, 0777); err != nil {
			return errors.Wrapf(err, "create directory %s for plugin", dir)
		}
		writer, err := os.Create(pluginFile)
		if err != nil {
			return errors.Wrapf(err, "create plugin file %s", f.Path)
		}
		defer writer.Close()
		reader := strings.NewReader(f.Content)
		if _, err := io.Copy(writer, reader); err != nil {
			return errors.Wrapf(err, "write plugin file %s", f.Path)
		}
		// If the Filename property of the current file matches the Filename
		// property of the plugin, this is the entry point, so set it to be

View on GitHub (pinned to d624239058)

Solutions

  1. Package the plugin as a single self-contained file before installing
  2. Remove extra entries from the files array in plugin.json so only the entry-point script remains
  3. Inline auxiliary library files into the main script
  4. Use a newer installer/registry that supports multi-file plugins if available

Example fix

// before (plugin.json)
{ "files": ["main.5m.py", "helper.py"] }
// after
{ "files": ["main.5m.py"] } // helper code inlined into main.5m.py
Defensive patterns

Strategy: validation

Validate before calling

// before Install: enforce single-file plugins
if len(plugin.Files) > 1 {
    return fmt.Errorf("plugin %q has %d files; only single-file plugins can be installed", plugin.Name, len(plugin.Files))
}

Try / catch

// Go
if err := installer.Install(ctx, pluginPath); err != nil {
    if strings.Contains(err.Error(), "only one plugin file supported") {
        return fmt.Errorf("bundle this plugin into one file before installing: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling Install with a metadata.Plugin whose Files slice contains 2 or more entries — e.g. multi-file plugins or plugin.json listing extra assets.

Common situations: Installing a plugin bundle that ships a script plus a companion library file; plugin authors adding READMEs or assets to the files array; future xbar versions supporting multi-file while this installer does not.

Related errors


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