gradle/gradle · error · PublishException
Cannot publish module metadata because an artifact from the
Error message
Cannot publish module metadata because an artifact from the '{}' component has been removed. The available artifacts had these problems:
{} What it means
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.
Source
Thrown at platforms/software/maven/src/main/java/org/gradle/api/publish/maven/internal/validation/MavenPublicationErrorChecker.java:83
for (MavenArtifact mavenArtifact : mainArtifacts) {
EnumSet<ArtifactDifference> differenceSet = EnumSet.noneOf(ArtifactDifference.class);
if (!source.getFile().equals(mavenArtifact.getFile())) {
differenceSet.add(ArtifactDifference.FILE);
}
// Necessary as the classifier can be converted from an empty string to null
if (!Strings.nullToEmpty(source.getClassifier()).equals(Strings.nullToEmpty(mavenArtifact.getClassifier()))) {
differenceSet.add(ArtifactDifference.CLASSIFIER);
}
if (!source.getExtension().equals(mavenArtifact.getExtension())) {
differenceSet.add(ArtifactDifference.EXTENSION);
}
// If it's all equal, we found a matching artifact that is being published
if (differenceSet.isEmpty()) {
return;
}
differences.put(mavenArtifact, differenceSet);
}
throw new PublishException("Cannot publish module metadata because an artifact from the '" + componentName +
"' component has been removed. The available artifacts had these problems:\n" + formatDifferences(projectDisplayName, buildDir, source, differences));
}
private static final Comparator<Set<ArtifactDifference>> DIFFERENCE_SET_COMPARATOR =
// Put the artifacts with the least differences first, since they're more likely to be useful
Comparator.<Set<ArtifactDifference>>comparingInt(Set::size)
// Prefer FILE differences over CLASSIFIER differences over EXTENSION differences,
// since different classifiers/extensions are unlikely to be right
.thenComparing(set -> set.contains(ArtifactDifference.FILE))
.thenComparing(set -> set.contains(ArtifactDifference.CLASSIFIER))
.thenComparing(set -> set.contains(ArtifactDifference.EXTENSION));
private static final Comparator<Map.Entry<MavenArtifact, Set<ArtifactDifference>>> DIFFERENCE_ENTRY_COMPARATOR =
Map.Entry.<MavenArtifact, Set<ArtifactDifference>>comparingByValue(DIFFERENCE_SET_COMPARATOR)
// Last ditch effort to make the order deterministic
.thenComparing(entry -> entry.getKey().getFile().toPath());
private static String formatDifferences(String projectDisplayName, Path buildDir, PublishArtifact source, Map<MavenArtifact, Set<ArtifactDifference>> differencesByArtifact) {View on GitHub (pinned to 534f27719b)
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
Example fix
// before
java { withSourcesJar() }
publishing.publications.maven.artifacts.removeIf { it.classifier == 'sources' } // module metadata check fails
// after
// do not add the sources variant to the component at all
java { /* no withSourcesJar() */ } Defensive patterns
Strategy: validation
Validate before calling
gradle.projectsEvaluated {
tasks.withType(GenerateModuleMetadata).configureEach { meta ->
// surface the mismatch at generation time rather than at publish time
meta.doLast {
logger.lifecycle("module metadata for ${meta.path} generated - verify publication artifacts were not filtered")
}
}
} Try / catch
try {
publishTask.publish()
} catch (org.gradle.api.publish.PublishException e) {
if (e.message?.contains('has been removed')) { /* restore the artifact or trim the component's variants */ }
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Maven publication '%s' cannot include multiple components
- Cannot publish a dependency with an artifact name different
- Unsupported dependency type: {}
- Artifact {} wasn't produced by this build.
- POM file is invalid. Check any modifications you have made t
AI-assisted analysis of gradle/gradle@534f27719b (2026-08-22).
Data as JSON: /api/errors/e232147cd4d8dfb8.
Report an issue: GitHub.