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

  1. Restore the removed artifact - keep every component artifact in the publication
  2. Remove the artifact from the component/variant as well instead of from the publication (e.g. do not call withSourcesJar())
  3. 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

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


AI-assisted analysis of gradle/gradle@534f27719b (2026-08-22). Data as JSON: /api/errors/e232147cd4d8dfb8. Report an issue: GitHub.