{"record":{"id":"336c3e43e8627c98","repo":"gradle/gradle","slug":"s-could-not-find-plugin-descriptor-of-s-at-meta","errorCode":null,"errorMessage":"%s: Could not find plugin descriptor of %s at META-INF/gradle-plugins/%s.properties","messagePattern":"(.+?): Could not find plugin descriptor of (.+?) at META-INF/gradle-plugins/(.+?)\\.properties","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"platforms/extensibility/plugin-development/src/main/java/org/gradle/plugin/devel/plugins/JavaGradlePluginPlugin.java","lineNumber":388,"sourceCode":"                        descriptorURI = descriptor.getPropertiesFileUrl().toURI();\n                    } catch (URISyntaxException e) {\n                        // Do nothing since the only side effect is that we wouldn't\n                        // be able to log the plugin descriptor file name.  Shouldn't\n                        // be a reasonable scenario where this occurs since these\n                        // descriptors should be generated from real files.\n                    }\n                    String pluginFileName = descriptorURI != null ? new File(descriptorURI).getName() : \"UNKNOWN\";\n                    pluginFileNames.add(pluginFileName);\n                    String pluginImplementation = descriptor.getImplementationClassName();\n                    if (pluginImplementation.length() == 0) {\n                        LOGGER.warn(String.format(INVALID_DESCRIPTOR_WARNING_MESSAGE, task.getPath(), pluginFileName));\n                    } else if (!hasFullyQualifiedClass(pluginImplementation)) {\n                        LOGGER.warn(String.format(BAD_IMPL_CLASS_WARNING_MESSAGE, task.getPath(), pluginFileName, pluginImplementation));\n                    }\n                }\n                for (PluginDeclaration declaration : plugins.get()) {\n                    if (!pluginFileNames.contains(declaration.getId() + \".properties\")) {\n                        LOGGER.warn(String.format(DECLARED_PLUGIN_MISSING_MESSAGE, task.getPath(), declaration.getName(), declaration.getId()));\n                    }\n                }\n            }\n        }\n\n        boolean hasFullyQualifiedClass(String fqClass) {\n            return actionsState.getCollectedClasses().contains(fqClass.replaceAll(\"\\\\.\", \"/\") + \".class\");\n        }\n    }\n\n    /**\n     * A file copy action that collects plugin descriptors as they are added to the jar.\n     */\n    static class PluginDescriptorCollectorAction implements Action<FileCopyDetails> {\n        private final PluginValidationActionsState actionsState;\n\n        PluginDescriptorCollectorAction(PluginValidationActionsState actionsState) {\n            this.actionsState = actionsState;","sourceCodeStart":370,"sourceCodeEnd":406,"githubUrl":"https://github.com/gradle/gradle/blob/534f27719b66953f95cc907aae7f2c1b12f5482d/platforms/extensibility/plugin-development/src/main/java/org/gradle/plugin/devel/plugins/JavaGradlePluginPlugin.java#L370-L406","documentation":"For every gradlePlugin.plugins declaration, validation expects a matching descriptor file named <id>.properties in META-INF/gradle-plugins inside the jar. DECLARED_PLUGIN_MISSING_MESSAGE fires when the declaration exists but no such file name was collected: the declaration and the jar contents are out of sync.","triggerScenarios":"Declaring a plugin with an id while descriptor generation or placement was customized away (processResources/source set changes), the id in the declaration not matching the generated file name, or resources re-routed so the .properties never lands in the jar.","commonSituations":"Custom processResources or sourceSets configuration breaking descriptor generation; renaming plugin ids without regenerating descriptors; disabling the generate-plugin-descriptors output.","solutions":["Align the declaration id with the descriptor file name: id 'com.example.greeting' must produce META-INF/gradle-plugins/com.example.greeting.properties.","Undo source-set or processResources customizations that exclude or relocate generated descriptors.","Build the jar and list META-INF/gradle-plugins to see which files actually shipped.","Re-run ./gradlew validatePlugins to confirm every declaration maps to a file."],"exampleFix":"// before: declaration id does not match shipped descriptor\ngradlePlugin {\n    plugins {\n        create('greeting') { id = 'com.example.greet' } // expects com.example.greet.properties\n    }\n}\n// jar contains META-INF/gradle-plugins/com.example.greeting.properties\n\n// after: make them match\ncreate('greeting') { id = 'com.example.greeting' }","handlingStrategy":"validation","validationCode":"tasks.register('checkDescriptorIds') {\n    dependsOn jar\n    doLast {\n        new ZipFile(jar.archiveFile.get().asFile).withCloseable { zf ->\n            gradlePlugin.plugins.each { d ->\n                def entry = \"META-INF/gradle-plugins/${d.id}.properties\"\n                if (zf.getEntry(entry) == null) {\n                    throw new GradleException(\"declaration ${d.id} has no ${entry} in jar\")\n                }\n            }\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep declaration ids exactly equal to the descriptor file base name.","Do not customize processResources or source sets in ways that exclude generated descriptors.","Inspect the jar's META-INF/gradle-plugins directory whenever descriptor wiring changes."],"tags":["gradle","plugin-development","descriptor","declaration"],"backgroundTag":"plugin-descriptor-missing","analyzedSha":"534f27719b66953f95cc907aae7f2c1b12f5482d","analyzedAt":"2026-08-22T08:09:12.375Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}