{"record":{"id":"3c09430717af5642","repo":"elastic/elasticsearch","slug":"classname-is-a-required-setting-for-esplugin","errorCode":null,"errorMessage":"classname is a required setting for esplugin","messagePattern":"classname is a required setting for esplugin","errorType":"validation","errorClass":"InvalidUserDataException","httpStatus":null,"severity":"error","filePath":"build-tools/src/main/java/org/elasticsearch/gradle/plugin/GeneratePluginPropertiesTask.java","lineNumber":113,"sourceCode":"    @PathSensitive(PathSensitivity.RELATIVE)\n    public abstract ConfigurableFileCollection getModuleInfoFile();\n\n    @OutputFile\n    public abstract RegularFileProperty getOutputFile();\n\n    @Input\n    public abstract Property<Boolean> getIsStable();\n\n    @Input\n    @Optional\n    public abstract Property<String> getDeploymentTarget();\n\n    @TaskAction\n    public void generatePropertiesFile() throws IOException {\n        String classname = getClassname().getOrElse(\"\");\n        boolean stablePlugin = getIsStable().getOrElse(false);\n        if (stablePlugin == false && classname.isEmpty()) {\n            throw new InvalidUserDataException(\"classname is a required setting for esplugin\");\n        }\n        if (stablePlugin && classname.isEmpty() == false) {\n            throw new InvalidUserDataException(\"classname is a forbidden for stable esplugin\");\n        }\n\n        Map<String, Object> props = new HashMap<>();\n        props.put(\"name\", getPluginName().get());\n        props.put(\"description\", getPluginDescription().get());\n        props.put(\"version\", getPluginVersion().get());\n        props.put(\"elasticsearchVersion\", getElasticsearchVersion().get());\n        props.put(\"javaVersion\", getJavaVersion().get());\n        props.put(\"classname\", classname);\n        props.put(\"extendedPlugins\", String.join(\",\", getExtendedPlugins().get()));\n        props.put(\"hasNativeController\", getHasNativeController().get());\n        props.put(\"nativeControllerEnabledSettings\", String.join(\",\", getNativeControllerEnabledSettings().get()));\n        props.put(\"requiresKeystore\", getRequiresKeystore().get());\n        props.put(\"licensed\", getIsLicensed().get());\n        props.put(\"modulename\", findModuleName());","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/build-tools/src/main/java/org/elasticsearch/gradle/plugin/GeneratePluginPropertiesTask.java#L95-L131","documentation":"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.","triggerScenarios":"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).","commonSituations":"Authoring a new Elasticsearch plugin and forgetting the `classname 'com.acme.FooPlugin'` line, or renaming/removing the Plugin subclass without updating the esplugin block.","solutions":["Add `classname '<fully.qualified.PluginClass>'` to the esplugin { } block, pointing at a class extending org.elasticsearch.plugins.Plugin.","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).","Verify the referenced class exists in the project's main source set and compiles."],"exampleFix":"// before\nesplugin {\n  name 'my-plugin'\n  description '...'\n  // classname missing\n}\n\n// after\nesplugin {\n  name 'my-plugin'\n  description '...'\n  classname 'com.acme.MyPlugin'\n}","handlingStrategy":"validation","validationCode":"// In a Gradle init script or convention plugin, validate before the task runs\nproject.tasks.withType(GeneratePluginPropertiesTask.class).configureEach {\n    if (!it.isStable.get() && it.classname.getOrElse('').trim().isEmpty()) {\n        throw new GradleException(\"esplugin '${it.pluginName.get()}' must set classname (or set isStable=true)\")\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat classname and isStable as mutually exclusive toggles and lint for it in a convention plugin.","When scaffolding a new plugin, template the esplugin block with classname pre-filled.","Run generatePluginPropertiesTask early in CI to catch misconfiguration before the heavier build."],"tags":["build","plugin","configuration","validation"],"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T12:17:08.281Z"}