elastic/elasticsearch · error · InvalidUserDataException

classname is a required setting for esplugin

Error message

classname is a required setting for esplugin

What it means

Thrown by GeneratePluginPropertiesTask.generatePropertiesFile when an esplugin declaration omits classname and the plugin is NOT marked stable. The task needs a plugin entry-point class name to emit into plugin-descriptor.properties, so a non-stable plugin without one is misconfigured.

Source

Thrown at build-tools/src/main/java/org/elasticsearch/gradle/plugin/GeneratePluginPropertiesTask.java:113

    @PathSensitive(PathSensitivity.RELATIVE)
    public abstract ConfigurableFileCollection getModuleInfoFile();

    @OutputFile
    public abstract RegularFileProperty getOutputFile();

    @Input
    public abstract Property<Boolean> getIsStable();

    @Input
    @Optional
    public abstract Property<String> getDeploymentTarget();

    @TaskAction
    public void generatePropertiesFile() throws IOException {
        String classname = getClassname().getOrElse("");
        boolean stablePlugin = getIsStable().getOrElse(false);
        if (stablePlugin == false && classname.isEmpty()) {
            throw new InvalidUserDataException("classname is a required setting for esplugin");
        }
        if (stablePlugin && classname.isEmpty() == false) {
            throw new InvalidUserDataException("classname is a forbidden for stable esplugin");
        }

        Map<String, Object> props = new HashMap<>();
        props.put("name", getPluginName().get());
        props.put("description", getPluginDescription().get());
        props.put("version", getPluginVersion().get());
        props.put("elasticsearchVersion", getElasticsearchVersion().get());
        props.put("javaVersion", getJavaVersion().get());
        props.put("classname", classname);
        props.put("extendedPlugins", String.join(",", getExtendedPlugins().get()));
        props.put("hasNativeController", getHasNativeController().get());
        props.put("nativeControllerEnabledSettings", String.join(",", getNativeControllerEnabledSettings().get()));
        props.put("requiresKeystore", getRequiresKeystore().get());
        props.put("licensed", getIsLicensed().get());
        props.put("modulename", findModuleName());

View on GitHub (pinned to db6a809a66)

Solutions

  1. Add `classname '<fully.qualified.PluginClass>'` to the esplugin { } block, pointing at a class extending org.elasticsearch.plugins.Plugin.
  2. If the module is a stable plugin (no real Plugin class), set `isStable = true` instead so classname is intentionally omitted (see error 42 for the inverse rule).
  3. Verify the referenced class exists in the project's main source set and compiles.

Example fix

// before
esplugin {
  name 'my-plugin'
  description '...'
  // classname missing
}

// after
esplugin {
  name 'my-plugin'
  description '...'
  classname 'com.acme.MyPlugin'
}
Defensive patterns

Strategy: validation

Validate before calling

// In a Gradle init script or convention plugin, validate before the task runs
project.tasks.withType(GeneratePluginPropertiesTask.class).configureEach {
    if (!it.isStable.get() && it.classname.getOrElse('').trim().isEmpty()) {
        throw new GradleException("esplugin '${it.pluginName.get()}' must set classname (or set isStable=true)")
    }
}

Prevention

When it happens

Trigger: A Gradle project applies the esplugin plugin and calls esplugin { ... } without setting classname, while isStable defaults to false. The task runs at generation time (the generatePropertiesFile @TaskAction).

Common situations: Authoring a new Elasticsearch plugin and forgetting the `classname 'com.acme.FooPlugin'` line, or renaming/removing the Plugin subclass without updating the esplugin block.

Related errors


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