elastic/elasticsearch · error · PluginSyncException
Duplicate plugin ID [{id}] found in [elasticsearch-plugins.y
Error message
Duplicate plugin ID [{id}] found in [elasticsearch-plugins.yml] What it means
Thrown by PluginsConfig.validate when two entries in elasticsearch-plugins.yml share the same plugin id. After the null-id check, the code accumulates ids into a HashSet and throws PluginSyncException the first time `uniquePluginIds.add(...)` returns false. Unlike the null-id error, this is a typed PluginSyncException so callers can distinguish it.
Source
Thrown at distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/PluginsConfig.java:74
* <li>All {@link InstallablePlugin}s must have IDs</li>
* <li>Any proxy must be well-formed.</li>
* <li>Unofficial plugins must have locations</li>
* </ul>
*
* @param officialPlugins the plugins that can be installed by name only
* @param migratedPlugins plugins that were once official but have since become modules. These
* plugin IDs can still be specified, but do nothing.
* @throws PluginSyncException if validation problems are found
*/
public void validate(Set<String> officialPlugins, Set<String> migratedPlugins) throws PluginSyncException {
if (this.plugins.stream().anyMatch(each -> each == null || Strings.isNullOrBlank(each.getId()))) {
throw new RuntimeException("Cannot have null or empty IDs in [elasticsearch-plugins.yml]");
}
final Set<String> uniquePluginIds = new HashSet<>();
for (final InstallablePlugin plugin : plugins) {
if (uniquePluginIds.add(plugin.getId()) == false) {
throw new PluginSyncException("Duplicate plugin ID [" + plugin.getId() + "] found in [elasticsearch-plugins.yml]");
}
}
for (InstallablePlugin plugin : this.plugins) {
if (officialPlugins.contains(plugin.getId()) == false
&& migratedPlugins.contains(plugin.getId()) == false
&& plugin.getLocation() == null) {
throw new PluginSyncException(
"Must specify location for non-official plugin [" + plugin.getId() + "] in [elasticsearch-plugins.yml]"
);
}
}
if (this.proxy != null) {
final String[] parts = this.proxy.split(":");
if (parts.length != 2) {
throw new PluginSyncException("Malformed [proxy], expected [host:port] in [elasticsearch-plugins.yml]");
}View on GitHub (pinned to db6a809a66)
Solutions
- Dedupe elasticsearch-plugins.yml so each id appears exactly once.
- If you need the same plugin in different roles, that is not supported via duplicate ids — keep one entry.
- Lint the file: `yq '.plugins | map(.id) | group_by(.) | map(select(length > 1))' elasticsearch-plugins.yml` should return [].
Example fix
# before: plugins: - id: analysis-icu - id: analysis-icu # duplicate # after: plugins: - id: analysis-icu
Defensive patterns
Strategy: validation
Validate before calling
// Dedupe ids before validation.
List<String> ids = parsed.getPlugins().stream().map(InstallablePlugin::getId).toList();
if (new HashSet<>(ids).size() != ids.size()) {
throw new IllegalArgumentException("Duplicate plugin ids in elasticsearch-plugins.yml");
} Prevention
- Lint with `yq '.plugins|map(.id)|group_by(.)|map(select(length>1))'`.
- Generate the file from a single source of truth (e.g. a Helm values list).
- Run PluginsConfig.validate in a dry-run step before rolling out.
When it happens
Trigger: The plugins list contains two InstallablePlugin entries with identical getId(); the second add() to uniquePluginIds fails and the duplicate id is reported in the message.
Common situations: Operator lists the same official plugin twice; merged config files duplicate an entry; templating system rendered a plugin block twice for different nodes into one file.
Related errors
- Cannot have null or empty IDs in [elasticsearch-plugins.yml]
- Must specify location for non-official plugin [{id}] in [ela
- Malformed [proxy], expected [host:port] in [elasticsearch-pl
- Empty location for plugin [{id}]
- Malformed location for plugin [{id}]
AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12).
Data as JSON: /api/errors/5d2fb172e27b4e5a.
Report an issue: GitHub.