elastic/elasticsearch · error · UserException

74

74

Error message

bin dir for {pluginId} is not a directory

What it means

Thrown by RemovePluginAction.checkCanRemove when a plugin's bin directory exists at env.binDir().resolve(pluginId) but is not a directory (i.e. it is a regular file or special node). Exit code IO_ERROR (74). This guards removePlugin from trying to treat a non-directory path as a directory.

Source

Thrown at distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/RemovePluginAction.java:153

            if (PLUGINS_CONVERTED_TO_MODULES.contains(pluginId)) {
                terminal.errorPrintln(
                    "plugin [" + pluginId + "] is no longer a plugin but instead a module packaged with this distribution of Elasticsearch"
                );
            } else {
                final String message = String.format(
                    Locale.ROOT,
                    "plugin [%s] not found; run 'elasticsearch-plugin list' to get list of installed plugins",
                    pluginId
                );
                throw new UserException(ExitCodes.CONFIG, message);
            }
        }

        final Path pluginBinDir = env.binDir().resolve(pluginId);
        if (Files.exists(pluginBinDir)) {
            if (Files.isDirectory(pluginBinDir) == false) {
                throw new UserException(ExitCodes.IO_ERROR, "bin dir for " + pluginId + " is not a directory");
            }
        }
    }

    private void removePlugin(InstallablePlugin plugin) throws IOException {
        final 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);

        terminal.println("-> removing [" + pluginId + "]...");

        final List<Path> pluginPaths = new ArrayList<>();

        /*
         * Add the contents of the plugin directory before creating the marker file and adding it to the list of paths to be deleted so
         * that the marker file is the last file to be deleted.
         */

View on GitHub (pinned to db6a809a66)

Solutions

  1. Inspect `<ES_HOME>/bin/<pluginId>`: if it is a regular file, move/remove it so the path is either absent or a directory.
  2. Reinstall the plugin cleanly to recreate the proper bin directory, then remove via the CLI.
  3. Avoid manual file creation under the bin root; use the CLI for all install/remove operations.

Example fix

# before: <ES_HOME>/bin/analysis-icu is a file
ls -ld <ES_HOME>/bin/analysis-icu
# remove the stray file:
rm <ES_HOME>/bin/analysis-icu
# then retry:
bin/elasticsearch-plugin remove analysis-icu
Defensive patterns

Strategy: validation

Validate before calling

// Check the plugin bin path is a directory before removal.
Path binDir = env.binDir().resolve(id);
if (Files.exists(binDir) && !Files.isDirectory(binDir)) {
    throw new IllegalStateException(binDir + " is not a directory; investigate manually");
}

Prevention

When it happens

Trigger: After the not-found check, the code resolves pluginBinDir; if `Files.exists(pluginBinDir)` is true but `Files.isDirectory(pluginBinDir)` is false, it throws. Reached when something created a regular file at `<binDir>/<pluginId>` where a directory was expected.

Common situations: A stray file (log, tarball fragment, symlink to a file) was left at the plugin's bin path; manual cleanup that `touch`ed the path; filesystem corruption or a bad extraction leaving a file where the directory should be.

Related errors


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