{"record":{"id":"67a3bc20515ea692","repo":"matryer/xbar","slug":"error-fetching-plugin-s-s","errorCode":null,"errorMessage":"error fetching plugin %s: %s","messagePattern":"error fetching plugin (.+?): (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/plugins/install.go","lineNumber":69,"sourceCode":"\t}\n\tif err := i.writePluginFiles(dest, plugin); err != nil {\n\t\treturn \"\", errors.Wrap(err, \"writePluginFiles\")\n\t}\n\tinstalledPluginPath, err := filepath.Rel(i.PluginDir, dest)\n\tif err != nil {\n\t\treturn \"\", errors.Wrap(err, \"filepath.Rel\")\n\t}\n\treturn installedPluginPath, nil\n}\n\n// fetchPlugin fetches the plugin metadata and file contents from the xbar website.\nfunc (i Installer) fetchPlugin(pluginPath *url.URL) (metadata.Plugin, error) {\n\tresp, err := i.Client.Get(pluginPath.String())\n\tif err != nil {\n\t\treturn metadata.Plugin{}, err\n\t}\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn metadata.Plugin{}, errors.Errorf(\"error fetching plugin %s: %s\", pluginPath, resp.Status)\n\t}\n\tdefer resp.Body.Close()\n\tvar responseBody struct {\n\t\tPlugin metadata.Plugin `json:\"plugin\"`\n\t}\n\tif err := json.NewDecoder(resp.Body).Decode(&responseBody); err != nil {\n\t\treturn metadata.Plugin{}, errors.Wrapf(err, \"error decoding plugin %s\", pluginPath)\n\t}\n\treturn responseBody.Plugin, nil\n}\n\n// getInstalledPluginName builds a name for the file or folder that will be\n// created in the plugin installation directory. Since a plugin can be installed\n// multiple times, each installed plugin will be given a sequence number to\n// ensure unique naming in the filesystem.\nfunc (i Installer) getInstalledPluginName(plugin metadata.Plugin) (string, error) {\n\tvar (\n\t\tcount         int","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/matryer/xbar/blob/d624239058997c80118eaebe2e7f8331b3c765e0/pkg/plugins/install.go#L51-L87","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","solutions":["Verify the plugin URL/path is correct and the plugin exists in the registry","Retry if the status was a transient 5xx","Check network connectivity and proxy/firewall settings","Add authentication if the plugin host requires it"],"exampleFix":"// before\nerr := installer.Install(ctx, \"xbar-repo/nonexistent-plugin\")\n// after\n// verify the plugin exists first, e.g. browse the registry for the exact path,\n// then install:\nerr := installer.Install(ctx, \"xbar-repo/real-plugin.1h.py\")","handlingStrategy":"retry","validationCode":"// before Install: probe the URL\nresp, err := installer.Client.Head(pluginURL.String())\nif err != nil || resp.StatusCode != http.StatusOK {\n    return fmt.Errorf(\"plugin not reachable at %s (status %v)\", pluginURL, err)\n}","typeGuard":null,"tryCatchPattern":"// Go: retry transient failures\nvar err error\nfor attempt := 0; attempt < 3; attempt++ {\n    err = installer.Install(ctx, pluginPath)\n    if err == nil || !strings.Contains(err.Error(), \"error fetching plugin\") || !isRetryable(err) {\n        break\n    }\n    time.Sleep(time.Duration(1<<attempt) * time.Second)\n}","preventionTips":["Double-check plugin paths/URLs against the registry listing","Handle 401/403 by configuring credentials for private repos","Surface resp.Status to users for actionable messages","Prefer retrying only 5xx and network errors, not 4xx"],"tags":["network","http","plugins","install"],"backgroundTag":"http-non-200-response","analyzedSha":"d624239058997c80118eaebe2e7f8331b3c765e0","analyzedAt":"2026-09-02T22:38:22.007Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}