helm/helm · error

failed to uninstall plugin %s, got error (%w)

Error message

failed to uninstall plugin %s, got error (%w)

What it means

`helm plugin uninstall NAME...` removes each plugin's directory with os.RemoveAll, deletes cached versioned tarballs, and runs the plugin's delete hook (pkg/cmd/plugin_uninstall.go:87-122). Any per-plugin failure is collected and the batch is returned via errors.Join, so one bad plugin does not stop the others but the command still fails overall. Note a missing plugin produces the distinct 'plugin: %s not found' error instead.

Source

Thrown at pkg/cmd/plugin_uninstall.go:73

func (o *pluginUninstallOptions) complete(args []string) error {
	if len(args) == 0 {
		return errors.New("please provide plugin name to uninstall")
	}
	o.names = args
	return nil
}

func (o *pluginUninstallOptions) run(out io.Writer) error {
	slog.Debug("loading installer plugins", "dir", settings.PluginsDirectory)
	plugins, err := plugin.LoadAllDir(settings.PluginsDirectory, plugin.LogIgnorePluginLoadErrorFilterFunc)
	if err != nil {
		return err
	}
	var errorPlugins []error
	for _, name := range o.names {
		if found := findPlugin(plugins, name); found != nil {
			if err := uninstallPlugin(found); err != nil {
				errorPlugins = append(errorPlugins, fmt.Errorf("failed to uninstall plugin %s, got error (%w)", name, err))
			} else {
				fmt.Fprintf(out, "Uninstalled plugin: %s\n", name)
			}
		} else {
			errorPlugins = append(errorPlugins, fmt.Errorf("plugin: %s not found", name))
		}
	}
	if len(errorPlugins) > 0 {
		return errors.Join(errorPlugins...)
	}
	return nil
}

func uninstallPlugin(p plugin.Plugin) error {
	if err := os.RemoveAll(p.Dir()); err != nil {
		return err
	}

View on GitHub (pinned to 2a29f1770b)

Solutions

  1. Fix ownership of the plugins directory (sudo chown -R $(id -u):$(id -g) $HELM_PLUGINS) or re-run the uninstall with sudo
  2. If the directory is stuck, remove it manually (rm -rf $HELM_PLUGINS/<name>) and confirm with `helm plugin list`
  3. For hook failures, inspect the plugin's delete hook script and run it manually to see its error

Example fix

# before
helm plugin uninstall my-plugin
# error: failed to uninstall plugin my-plugin, got error (unlinkat ...: permission denied)

# after
sudo chown -R "$(id -u):$(id -g)" "$(helm env HELM_PLUGINS)"
helm plugin uninstall my-plugin
Defensive patterns

Strategy: try-catch

Validate before calling

func canRemovePluginDir(dir string) error {
    fi, err := os.Stat(dir)
    if err != nil {
        return err
    }
    if !fi.IsDir() {
        return nil
    }
    probe := filepath.Join(dir, ".rm-probe")
    if err := os.WriteFile(probe, nil, 0o644); err != nil {
        return fmt.Errorf("no write access to %s (owned by another user?): %w", dir, err)
    }
    return os.Remove(probe)
}

Try / catch

if err := runPluginUninstall(names); err != nil {
    // errors.Join bundles per-plugin failures; split them for reporting
    type unwrapper interface{ Unwrap() []error }
    if u, ok := err.(unwrapper); ok {
        for _, e := range u.Unwrap() {
            log.Print(e) // handle each plugin independently (permissions vs not-found vs hook)
        }
    }
    return err
}

Prevention

When it happens

Trigger: Plugins directory owned by root while running as a normal user (os.RemoveAll permission denied); a file inside the plugin dir held open (Windows locks, running plugin process); the plugin's delete hook script exiting non-zero.

Common situations: Plugin installed with sudo but uninstalled without; manually placed plugins with odd ownership; antivirus or editors locking shared libraries during removal.

Related errors


AI-assisted analysis of helm/helm@2a29f1770b (2026-08-15). Data as JSON: /api/errors/0a1811d581345251. Report an issue: GitHub.