{"record":{"id":"09034415cde24e50","repo":"elastic/elasticsearch","slug":"classname-is-a-forbidden-for-stable-esplugin","errorCode":null,"errorMessage":"classname is a forbidden for stable esplugin","messagePattern":"classname is a forbidden for stable esplugin","errorType":"validation","errorClass":"InvalidUserDataException","httpStatus":null,"severity":"error","filePath":"build-tools/src/main/java/org/elasticsearch/gradle/plugin/GeneratePluginPropertiesTask.java","lineNumber":116,"sourceCode":"    @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());\n\n        String deploymentTarget = getDeploymentTarget().getOrElse(\"ALL\");\n        if (DEPLOYMENT_TARGETS.contains(deploymentTarget) == false) {","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/build-tools/src/main/java/org/elasticsearch/gradle/plugin/GeneratePluginPropertiesTask.java#L98-L134","documentation":"The mirror of error 41: thrown when a plugin declared as stable (isStable=true) ALSO supplies a classname. Stable plugins intentionally have no Plugin entry-point class, so supplying classname contradicts the stable contract and the task refuses to emit it.","triggerScenarios":"esplugin { isStable = true; classname = '...' } — both set at once. The second guard in generatePropertiesFile fires.","commonSituations":"Converting a regular plugin to a stable plugin and forgetting to remove classname, or copy-pasting an esplugin block and toggling isStable without clearing classname.","solutions":["Remove the `classname '...'` line from the esplugin block for stable plugins.","If you actually need a Plugin entry class, set `isStable = false` (the default) so classname becomes required instead of forbidden.","Re-run the generatePluginPropertiesTask to confirm it emits a descriptor with no java policy/plugin entry class."],"exampleFix":"// before\nesplugin {\n  name 'my-stable-plugin'\n  isStable = true\n  classname 'com.acme.MyPlugin'  // forbidden when stable\n}\n\n// after\nesplugin {\n  name 'my-stable-plugin'\n  isStable = true\n  // no classname\n}","handlingStrategy":"validation","validationCode":"project.tasks.withType(GeneratePluginPropertiesTask.class).configureEach {\n    if (it.isStable.get() && !it.classname.getOrElse('').trim().isEmpty()) {\n        throw new GradleException(\"esplugin '${it.pluginName.get()}' is stable — remove classname\")\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Document the stable-plugin contract: no Plugin entry class.","Add a convention-plugin check forbidding classname when isStable=true.","When flipping isStable, audit the esplugin block for leftover classname."],"tags":["build","plugin","configuration","validation"],"backgroundTag":null,"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}