{"record":{"id":"5d2fb172e27b4e5a","repo":"elastic/elasticsearch","slug":"duplicate-plugin-id-id-found-in-elasticsearch","errorCode":null,"errorMessage":"Duplicate plugin ID [{id}] found in [elasticsearch-plugins.yml]","messagePattern":"Duplicate plugin ID \\[(.+?)\\] found in \\[elasticsearch-plugins\\.yml\\]","errorType":"exception","errorClass":"PluginSyncException","httpStatus":null,"severity":"error","filePath":"distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/PluginsConfig.java","lineNumber":74,"sourceCode":"     *     <li>All {@link InstallablePlugin}s must have IDs</li>\n     *     <li>Any proxy must be well-formed.</li>\n     *     <li>Unofficial plugins must have locations</li>\n     * </ul>\n     *\n     * @param officialPlugins the plugins that can be installed by name only\n     * @param migratedPlugins plugins that were once official but have since become modules. These\n     *                        plugin IDs can still be specified, but do nothing.\n     * @throws PluginSyncException if validation problems are found\n     */\n    public void validate(Set<String> officialPlugins, Set<String> migratedPlugins) throws PluginSyncException {\n        if (this.plugins.stream().anyMatch(each -> each == null || Strings.isNullOrBlank(each.getId()))) {\n            throw new RuntimeException(\"Cannot have null or empty IDs in [elasticsearch-plugins.yml]\");\n        }\n\n        final Set<String> uniquePluginIds = new HashSet<>();\n        for (final InstallablePlugin plugin : plugins) {\n            if (uniquePluginIds.add(plugin.getId()) == false) {\n                throw new PluginSyncException(\"Duplicate plugin ID [\" + plugin.getId() + \"] found in [elasticsearch-plugins.yml]\");\n            }\n        }\n\n        for (InstallablePlugin plugin : this.plugins) {\n            if (officialPlugins.contains(plugin.getId()) == false\n                && migratedPlugins.contains(plugin.getId()) == false\n                && plugin.getLocation() == null) {\n                throw new PluginSyncException(\n                    \"Must specify location for non-official plugin [\" + plugin.getId() + \"] in [elasticsearch-plugins.yml]\"\n                );\n            }\n        }\n\n        if (this.proxy != null) {\n            final String[] parts = this.proxy.split(\":\");\n            if (parts.length != 2) {\n                throw new PluginSyncException(\"Malformed [proxy], expected [host:port] in [elasticsearch-plugins.yml]\");\n            }","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/PluginsConfig.java#L56-L92","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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 []."],"exampleFix":"# before:\nplugins:\n  - id: analysis-icu\n  - id: analysis-icu    # duplicate\n# after:\nplugins:\n  - id: analysis-icu","handlingStrategy":"validation","validationCode":"// Dedupe ids before validation.\nList<String> ids = parsed.getPlugins().stream().map(InstallablePlugin::getId).toList();\nif (new HashSet<>(ids).size() != ids.size()) {\n    throw new IllegalArgumentException(\"Duplicate plugin ids in elasticsearch-plugins.yml\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["plugins-config","validation","yaml","plugin-cli"],"backgroundTag":null,"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T12:31:55.035Z"}