{"record":{"id":"1da4b99081838449","repo":"elastic/elasticsearch","slug":"64-1da4b9","errorCode":"64","errorMessage":"at least one plugin id is required","messagePattern":"at least one plugin id is required","errorType":"exception","errorClass":"UserException","httpStatus":null,"severity":"error","filePath":"distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/InstallPluginAction.java","lineNumber":209,"sourceCode":"\n    private final Terminal terminal;\n    private Environment env;\n    private boolean batch;\n    private Proxy proxy = null;\n\n    public InstallPluginAction(Terminal terminal, Environment env, boolean batch) {\n        this.terminal = terminal;\n        this.env = env;\n        this.batch = batch;\n    }\n\n    public void setProxy(Proxy proxy) {\n        this.proxy = proxy;\n    }\n\n    public void execute(List<InstallablePlugin> plugins) throws Exception {\n        if (plugins.isEmpty()) {\n            throw new UserException(ExitCodes.USAGE, \"at least one plugin id is required\");\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 UserException(ExitCodes.USAGE, \"duplicate plugin id [\" + plugin.getId() + \"]\");\n            }\n        }\n\n        final String logPrefix = terminal.isHeadless() ? \"\" : \"-> \";\n\n        final Map<String, List<Path>> deleteOnFailures = new LinkedHashMap<>();\n        for (final InstallablePlugin plugin : plugins) {\n            final String pluginId = plugin.getId();\n            terminal.println(logPrefix + \"Installing \" + pluginId);\n            try {\n                if (\"x-pack\".equals(pluginId)) {\n                    throw new UserException(ExitCodes.CONFIG, \"this distribution of Elasticsearch contains X-Pack by default\");","sourceCodeStart":191,"sourceCodeEnd":227,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/InstallPluginAction.java#L191-L227","documentation":"Thrown by `InstallPluginAction.execute(List<InstallablePlugin>)` when the plugins list is empty. Exits USAGE (64). This is the programmatic entry point (the CLI parsing layer builds the list from positional args before calling execute), so hitting it directly means a caller invoked execute with no plugins. The check runs before uniqueness validation and before any download.","triggerScenarios":"Calling `new InstallPluginAction(...).execute(emptyList())` directly; a CLI/wrapper that resolves plugin ids from config and resolves to none; misrouted invocation that dropped its args.","commonSituations":"Programmatic automation that conditionally installs plugins and the condition yielded an empty list; a custom CLI built on top of InstallPluginAction.","solutions":["Pass at least one InstallablePlugin in the list when calling execute.","Guard callers: `if (!plugins.isEmpty()) action.execute(plugins);`.","At the CLI level, ensure at least one plugin id positional arg is supplied."],"exampleFix":"// before\naction.execute(List.of());\n// after\naction.execute(List.of(new InstallablePlugin.Id(\"repository-s3\")));","handlingStrategy":"validation","validationCode":"if (plugins == null || plugins.isEmpty()) {\n    throw new IllegalArgumentException(\"InstallPluginAction.execute requires at least one plugin\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Guard callers: only invoke execute when the resolved plugin list is non-empty.","At the CLI layer, ensure at least one plugin id positional arg is parsed."],"tags":["elasticsearch","plugin-cli","cli-usage","argument-validation"],"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T08:17:17.861Z"}