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
- Remove the dependent plugins first, then the base — read the `used by` list in the message.
- List both the base and all its dependents in a single `remove` invocation.
- 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
- Maintain a dependency map of installed plugins and remove leaf-first.
- When scripting removals, include a plugin's dependents in the same batch.
- Surface the `used by` chain to operators before they schedule a removal.
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.