elastic/elasticsearch · error · UserException

11

11

Error message

Cannot remove plugins because the following are extended by other plugins:
	{key} used by {value}

What it means

Thrown by RemovePluginAction.ensurePluginsNotUsedByOtherPlugins when one or more of the plugins requested for removal are still extended/depended-on by other installed plugins. Exit code PLUGIN_STILL_USED (11) — a custom code specific to this case. The message lists every dependency edge (`key used by value`).

Source

Thrown at distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/RemovePluginAction.java:118

                    String pluginId = plugin.getId();
                    if (extendedPlugin.equals(pluginId)) {
                        usedBy.computeIfAbsent(entry.getKey(), (_key -> new ArrayList<>())).add(pluginId);
                    }
                }
            }
        }
        if (usedBy.isEmpty()) {
            return;
        }

        final StringJoiner message = new StringJoiner("\n");
        message.add("Cannot remove plugins because the following are extended by other plugins:");
        usedBy.forEach((key, value) -> {
            String s = "\t" + key + " used by " + value;
            message.add(s);
        });

        throw new UserException(PLUGIN_STILL_USED, message.toString());
    }

    private void checkCanRemove(InstallablePlugin plugin) throws UserException {
        String pluginId = plugin.getId();

        final Path pluginDir = env.pluginsDir().resolve(pluginId);
        final Path pluginConfigDir = env.configDir().resolve(pluginId);
        final Path removing = env.pluginsDir().resolve(".removing-" + pluginId);

        /*
         * If the plugin does not exist and the plugin config does not exist, fail to the user that the plugin is not found, unless there's
         * a marker file left from a previously failed attempt in which case we proceed to clean up the marker file. Or, if the plugin does
         * not exist, the plugin config does, and we are not purging, again fail to the user that the plugin is not found.
         */
        if ((Files.exists(pluginDir) == false && Files.exists(pluginConfigDir) == false && Files.exists(removing) == false)
            || (Files.exists(pluginDir) == false && Files.exists(pluginConfigDir) && this.purge == false)) {

            if (PLUGINS_CONVERTED_TO_MODULES.contains(pluginId)) {

View on GitHub (pinned to db6a809a66)

Solutions

  1. Remove the dependent plugins first, then the base — read the `used by` list in the message.
  2. List both the base and all its dependents in a single `remove` invocation.
  3. Re-run `elasticsearch-plugin list` and trace which plugin extends which before retrying.

Example fix

# before (fails):
bin/elasticsearch-plugin remove analysis-icu
# error: analysis-icu used by my-analysis-ext
# after (remove dependent first or together):
bin/elasticsearch-plugin remove my-analysis-ext analysis-icu
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-check dependents before removal.
Map<String, String> usedBy = buildUsedByMap(installedPlugins);
for (String id : idsToRemove) {
    if (usedBy.containsKey(id)) {
        throw new IllegalStateException(id + " is used by " + usedBy.get(id));
    }
}

Try / catch

try {
    removeAction.execute(plugins);
} catch (UserException e) {
    if (e.exitCode == RemovePluginAction.PLUGIN_STILL_USED) {
        // parse `used by` and offer to remove dependents too
        System.err.println("Remove dependents first: " + e.getMessage());
    }
    throw e;
}

Prevention

When it happens

Trigger: ensurePluginsNotUsedByOtherPlugins builds a `usedBy` map of plugin->dependents from the installed set; if it is non-empty, a multi-line UserException is thrown before any per-plugin checkCanRemove runs. Removing a base plugin while an extending plugin still references it triggers this.

Common situations: Trying to remove `analysis-icu` while a custom plugin extends it; removing a parent/joint plugin that another depends on; batch removal that lists only the dependency and not the dependent.

Related errors


AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12). Data as JSON: /api/errors/e74837fca38c38ae. Report an issue: GitHub.