{"record":{"id":"b05f254ffcd647cf","repo":"gradle/gradle","slug":"could-not-add-a-component-metadata-rule-for-module","errorCode":null,"errorMessage":"Could not add a component metadata rule for module '%s'.","messagePattern":"Could not add a component metadata rule for module '(.+?)'\\.","errorType":"validation","errorClass":"InvalidUserCodeException","httpStatus":null,"severity":"error","filePath":"platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/DefaultComponentMetadataHandler.java","lineNumber":115,"sourceCode":"        return this;\n    }\n\n    private ComponentMetadataHandler addClassBasedRule(SpecConfigurableRule ruleAction) {\n        metadataRuleContainer.addClassRule(ruleAction);\n        return this;\n    }\n\n    private <U> SpecRuleAction<? super U> createAllSpecRuleAction(RuleAction<? super U> ruleAction) {\n        return new SpecRuleAction<>(ruleAction, Specs.satisfyAll());\n    }\n\n    private SpecRuleAction<? super ComponentMetadataDetails> createSpecRuleActionForModule(Object id, RuleAction<? super ComponentMetadataDetails> ruleAction) {\n        ModuleIdentifier moduleIdentifier;\n\n        try {\n            moduleIdentifier = moduleIdentifierNotationParser.parseNotation(id);\n        } catch (UnsupportedNotationException e) {\n            throw new InvalidUserCodeException(String.format(INVALID_SPEC_ERROR, id == null ? \"null\" : id.toString()), e);\n        }\n\n        Spec<ComponentMetadataDetails> spec = new ComponentMetadataDetailsMatchingSpec(moduleIdentifier);\n        return new SpecRuleAction<>(ruleAction, spec);\n    }\n\n    @Override\n    public ComponentMetadataHandler all(Action<? super ComponentMetadataDetails> rule) {\n        return addRule(createAllSpecRuleAction(ruleActionAdapter.createFromAction(rule)));\n    }\n\n    @Override\n    public ComponentMetadataHandler all(Closure<?> rule) {\n        return addRule(createAllSpecRuleAction(ruleActionAdapter.createFromClosure(ComponentMetadataDetails.class, rule)));\n    }\n\n    @Override\n    @Deprecated","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/gradle/gradle/blob/534f27719b66953f95cc907aae7f2c1b12f5482d/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/DefaultComponentMetadataHandler.java#L97-L133","documentation":"components { withModule(id, actionRule) } / withModule(id) { details -> ... } parses id into a ModuleIdentifier using a notation parser that accepts 'group:name' strings (and map notation). If the notation is unsupported — most commonly a 3-part 'group:name:version' string, but also null, a Dependency object, or any random type — the UnsupportedNotationException is wrapped in InvalidUserCodeException: \"Could not add a component metadata rule for module '<id>'.\"","triggerScenarios":"components { withModule(\"org.springframework:spring-core:5.3.30\") { ... } } (version included), passing a Dependency or ExternalModule instance as id, passing null, or a map without group/name keys.","commonSituations":"Reusing a full GAV constant (shared with dependency declarations) as the withModule id; assuming withModule matches a specific version — it matches the module for ALL versions; copy-pasting coordinates from a dependencies block into the components block.","solutions":["Drop the version: withModule(\"org.springframework:spring-core\").","Use map notation if that reads better: withModule([group: 'org.springframework', name: 'spring-core']).","If you need version-specific behavior, keep the rule on the module only and check details.id.version inside the rule body before acting."],"exampleFix":"// before\ncomponents {\n    withModule(\"org.springframework:spring-core:5.3.30\") { // version -> unsupported notation\n        it.allVariants { v -> v.withDependencies { deps -> deps.removeAll() } }\n    }\n}\n\n// after\ncomponents {\n    withModule(\"org.springframework:spring-core\") {\n        it.allVariants { v -> v.withDependencies { deps -> deps.removeAll() } }\n    }\n}","handlingStrategy":"validation","validationCode":"def moduleId(String notation) {\n    def parts = notation.split(':')\n    assert parts.size() == 2 && parts.every { it } : \"withModule id must be 'group:name' (no version), got: '$notation'\"\n    notation\n}","typeGuard":"fun isModuleIdentifierNotation(id: Any?): Boolean =\n    id is String && id.split(\":\").let { it.size == 2 && it.all(String::isNotBlank) }","tryCatchPattern":null,"preventionTips":["Keep separate constants for module identifiers ('g:n') and dependency coordinates ('g:n:v'); never reuse one for the other.","Remember withModule matches every version of the module — version filtering belongs inside the rule body."],"tags":["gradle","component-metadata-rules","withmodule","notation"],"backgroundTag":"notation-parse-failed","analyzedSha":"534f27719b66953f95cc907aae7f2c1b12f5482d","analyzedAt":"2026-08-22T08:09:12.375Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}