elastic/elasticsearch · error · UserException
2
2
Error message
This plugin was built with an older plugin structure. Contact the plugin author to remove the intermediate "elasticsearch" directory within the plugin zip.
What it means
Thrown by unzip when a ZipEntry name starts with the literal prefix `elasticsearch/`. This indicates the plugin was packaged using the pre-5.x layout that nested everything under an intermediate `elasticsearch` directory. Modern installers expect the plugin's contents at the archive root. Exit code PLUGIN_MALFORMED (2).
Source
Thrown at distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/InstallPluginAction.java:759
: (HttpURLConnection) checksumUrl.openConnection(this.proxy);
if (connection.getResponseCode() == 404) {
return null;
}
return checksumUrl;
}
private Path unzip(Path zip, Path pluginsDir) throws IOException, UserException {
// unzip plugin to a staging temp dir
final Path target = stagingDirectory(pluginsDir);
pathsToDeleteOnShutdown.add(target);
try (ZipInputStream zipInput = new ZipInputStream(Files.newInputStream(zip))) {
ZipEntry entry;
byte[] buffer = new byte[8192];
while ((entry = zipInput.getNextEntry()) != null) {
if (entry.getName().startsWith("elasticsearch/")) {
throw new UserException(
PLUGIN_MALFORMED,
"This plugin was built with an older plugin structure."
+ " Contact the plugin author to remove the intermediate \"elasticsearch\" directory within the plugin zip."
);
}
Path targetFile = target.resolve(entry.getName());
// Using the entry name as a path can result in an entry outside of the plugin dir,
// either if the name starts with the root of the filesystem, or it is a relative
// entry like ../whatever. This check attempts to identify both cases by first
// normalizing the path (which removes foo/..) and ensuring the normalized entry
// is still rooted with the target plugin directory.
if (targetFile.normalize().startsWith(target) == false) {
throw new UserException(
PLUGIN_MALFORMED,
"Zip contains entry name '" + entry.getName() + "' resolving outside of plugin directory"
);
}View on GitHub (pinned to db6a809a66)
Solutions
- Contact the plugin author for a repackaged build (as the message says).
- Repackage the zip yourself by removing the top-level `elasticsearch/` directory so entries sit at the root.
- Use a maintained fork or the equivalent built-in module if one exists.
Example fix
# repackage mkdir /tmp/p && unzip old-plugin.zip -d /tmp/p cd /tmp/p/elasticsearch && zip -r ../fixed-plugin.zip . && cd .. elasticsearch-plugin install file:///tmp/fixed-plugin.zip
Defensive patterns
Strategy: validation
Validate before calling
try (ZipInputStream zis = new ZipInputStream(Files.newInputStream(zip))) {
ZipEntry e;
while ((e = zis.getNextEntry()) != null) {
if (e.getName().startsWith("elasticsearch/")) {
throw new IllegalStateException("Plugin uses legacy layout with intermediate elasticsearch/ dir");
}
}
} Prevention
- Before distributing, confirm your zip's entries sit at the archive root with no `elasticsearch/` prefix.
- Modernize the plugin build to the current packaging layout (entries directly under the plugin root).
When it happens
Trigger: Installing a plugin zip that someone assembled from an old Elasticsearch source tree; a third-party plugin that hasn't been repackaged for current ES versions.
Common situations: Legacy/unmaintained plugins; zips produced by old build tooling; archives copied from pre-5.x example trees.
Related errors
AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12).
Data as JSON: /api/errors/1022880c9b7a27ad.
Report an issue: GitHub.