matryer/xbar · error

error fetching plugin %s: %s

Error message

error fetching plugin %s: %s

What it means

fetchPlugin downloads plugin metadata over HTTP from the given URL. If the response status is anything other than 200 OK, it wraps the status into errors.Errorf("error fetching plugin %s: %s", pluginPath, resp.Status).

Source

Thrown at pkg/plugins/install.go:69

	}
	if err := i.writePluginFiles(dest, plugin); err != nil {
		return "", errors.Wrap(err, "writePluginFiles")
	}
	installedPluginPath, err := filepath.Rel(i.PluginDir, dest)
	if err != nil {
		return "", errors.Wrap(err, "filepath.Rel")
	}
	return installedPluginPath, nil
}

// fetchPlugin fetches the plugin metadata and file contents from the xbar website.
func (i Installer) fetchPlugin(pluginPath *url.URL) (metadata.Plugin, error) {
	resp, err := i.Client.Get(pluginPath.String())
	if err != nil {
		return metadata.Plugin{}, err
	}
	if resp.StatusCode != http.StatusOK {
		return metadata.Plugin{}, errors.Errorf("error fetching plugin %s: %s", pluginPath, resp.Status)
	}
	defer resp.Body.Close()
	var responseBody struct {
		Plugin metadata.Plugin `json:"plugin"`
	}
	if err := json.NewDecoder(resp.Body).Decode(&responseBody); err != nil {
		return metadata.Plugin{}, errors.Wrapf(err, "error decoding plugin %s", pluginPath)
	}
	return responseBody.Plugin, nil
}

// getInstalledPluginName builds a name for the file or folder that will be
// created in the plugin installation directory. Since a plugin can be installed
// multiple times, each installed plugin will be given a sequence number to
// ensure unique naming in the filesystem.
func (i Installer) getInstalledPluginName(plugin metadata.Plugin) (string, error) {
	var (
		count         int

View on GitHub (pinned to d624239058)

Solutions

  1. Verify the plugin URL/path is correct and the plugin exists in the registry
  2. Retry if the status was a transient 5xx
  3. Check network connectivity and proxy/firewall settings
  4. Add authentication if the plugin host requires it

Example fix

// before
err := installer.Install(ctx, "xbar-repo/nonexistent-plugin")
// after
// verify the plugin exists first, e.g. browse the registry for the exact path,
// then install:
err := installer.Install(ctx, "xbar-repo/real-plugin.1h.py")
Defensive patterns

Strategy: retry

Validate before calling

// before Install: probe the URL
resp, err := installer.Client.Head(pluginURL.String())
if err != nil || resp.StatusCode != http.StatusOK {
    return fmt.Errorf("plugin not reachable at %s (status %v)", pluginURL, err)
}

Try / catch

// Go: retry transient failures
var err error
for attempt := 0; attempt < 3; attempt++ {
    err = installer.Install(ctx, pluginPath)
    if err == nil || !strings.Contains(err.Error(), "error fetching plugin") || !isRetryable(err) {
        break
    }
    time.Sleep(time.Duration(1<<attempt) * time.Second)
}

Prevention

When it happens

Trigger: Calling Install with a plugin path URL whose GET returns 404 (plugin doesn't exist), 403, 500, etc. Any non-200 response from the plugin registry or repository triggers this.

Common situations: Typo in plugin path/URL; plugin removed from the registry; private repo without credentials; server outage returning 5xx; offline or DNS-proxied network returning error pages.

Related errors


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