{"record":{"id":"e232147cd4d8dfb8","repo":"gradle/gradle","slug":"cannot-publish-module-metadata-because-an-artifact","errorCode":null,"errorMessage":"Cannot publish module metadata because an artifact from the '{}' component has been removed. The available artifacts had these problems:\n{}","messagePattern":"Cannot publish module metadata because an artifact from the '(.+?)' component has been removed\\. The available artifacts had these problems:\n(.+?)","errorType":"exception","errorClass":"PublishException","httpStatus":null,"severity":"error","filePath":"platforms/software/maven/src/main/java/org/gradle/api/publish/maven/internal/validation/MavenPublicationErrorChecker.java","lineNumber":83,"sourceCode":"        for (MavenArtifact mavenArtifact : mainArtifacts) {\n            EnumSet<ArtifactDifference> differenceSet = EnumSet.noneOf(ArtifactDifference.class);\n            if (!source.getFile().equals(mavenArtifact.getFile())) {\n                differenceSet.add(ArtifactDifference.FILE);\n            }\n            // Necessary as the classifier can be converted from an empty string to null\n            if (!Strings.nullToEmpty(source.getClassifier()).equals(Strings.nullToEmpty(mavenArtifact.getClassifier()))) {\n                differenceSet.add(ArtifactDifference.CLASSIFIER);\n            }\n            if (!source.getExtension().equals(mavenArtifact.getExtension())) {\n                differenceSet.add(ArtifactDifference.EXTENSION);\n            }\n            // If it's all equal, we found a matching artifact that is being published\n            if (differenceSet.isEmpty()) {\n                return;\n            }\n            differences.put(mavenArtifact, differenceSet);\n        }\n        throw new PublishException(\"Cannot publish module metadata because an artifact from the '\" + componentName +\n            \"' component has been removed. The available artifacts had these problems:\\n\" + formatDifferences(projectDisplayName, buildDir, source, differences));\n    }\n\n    private static final Comparator<Set<ArtifactDifference>> DIFFERENCE_SET_COMPARATOR =\n        // Put the artifacts with the least differences first, since they're more likely to be useful\n        Comparator.<Set<ArtifactDifference>>comparingInt(Set::size)\n            // Prefer FILE differences over CLASSIFIER differences over EXTENSION differences,\n            // since different classifiers/extensions are unlikely to be right\n            .thenComparing(set -> set.contains(ArtifactDifference.FILE))\n            .thenComparing(set -> set.contains(ArtifactDifference.CLASSIFIER))\n            .thenComparing(set -> set.contains(ArtifactDifference.EXTENSION));\n\n    private static final Comparator<Map.Entry<MavenArtifact, Set<ArtifactDifference>>> DIFFERENCE_ENTRY_COMPARATOR =\n        Map.Entry.<MavenArtifact, Set<ArtifactDifference>>comparingByValue(DIFFERENCE_SET_COMPARATOR)\n            // Last ditch effort to make the order deterministic\n            .thenComparing(entry -> entry.getKey().getFile().toPath());\n\n    private static String formatDifferences(String projectDisplayName, Path buildDir, PublishArtifact source, Map<MavenArtifact, Set<ArtifactDifference>> differencesByArtifact) {","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/gradle/gradle/blob/534f27719b66953f95cc907aae7f2c1b12f5482d/platforms/software/maven/src/main/java/org/gradle/api/publish/maven/internal/validation/MavenPublicationErrorChecker.java#L65-L101","documentation":"When Gradle Module Metadata is published, every artifact of a component's variants must appear in the publication. MavenPublicationErrorChecker matches component artifacts against published Maven artifacts on name/classifier/extension; if no published artifact matches, a PublishException lists the differences and publishing is refused.","triggerScenarios":"Removing an artifact from a publication that still uses from(components.java), e.g. filtering out the main jar or the sources jar while the component keeps the variant that requires it.","commonSituations":"Builds that try to publish only a subset of artifacts (e.g. no sources jar) after java { withSourcesJar() } added them to the component; artifact-collection refactors that clear() or removeAll artifacts from publications.","solutions":["Restore the removed artifact - keep every component artifact in the publication","Remove the artifact from the component/variant as well instead of from the publication (e.g. do not call withSourcesJar())","Publish a custom component (or no component) that contains exactly the artifacts you publish"],"exampleFix":"// before\njava { withSourcesJar() }\npublishing.publications.maven.artifacts.removeIf { it.classifier == 'sources' } // module metadata check fails\n\n// after\n// do not add the sources variant to the component at all\njava { /* no withSourcesJar() */ }","handlingStrategy":"validation","validationCode":"gradle.projectsEvaluated {\n    tasks.withType(GenerateModuleMetadata).configureEach { meta ->\n        // surface the mismatch at generation time rather than at publish time\n        meta.doLast {\n            logger.lifecycle(\"module metadata for ${meta.path} generated - verify publication artifacts were not filtered\")\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    publishTask.publish()\n} catch (org.gradle.api.publish.PublishException e) {\n    if (e.message?.contains('has been removed')) { /* restore the artifact or trim the component's variants */ }\n}","preventionTips":["Never filter artifacts out of a publication backed by a component - remove them from the component instead","Do not call withSourcesJar()/withJavadocJar() if you intend to exclude those artifacts from publishing","Keep component variants and publication artifacts in sync; verify with publishToMavenLocal"],"tags":["gradle","maven","publishing","module-metadata","artifact-validation"],"backgroundTag":"module-metadata-artifact-mismatch","analyzedSha":"534f27719b66953f95cc907aae7f2c1b12f5482d","analyzedAt":"2026-08-22T08:09:12.375Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}