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

  1. Dedupe elasticsearch-plugins.yml so each id appears exactly once.
  2. If you need the same plugin in different roles, that is not supported via duplicate ids — keep one entry.
  3. 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

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


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