gradle/gradle · error · InvalidUserDataException

ivyModuleDescriptor implementations must implement the '%s'

Error message

ivyModuleDescriptor implementations must implement the '%s' interface, implementation '%s' does not

What it means

GenerateIvyDescriptor expects its descriptor property to hold Gradle's internal implementation type (IvyModuleDescriptorSpecInternal). Assigning a custom object that only implements the public IvyModuleDescriptorSpec interface is rejected with this InvalidUserDataException.

Source

Thrown at platforms/software/ivy/src/main/java/org/gradle/api/publish/ivy/tasks/GenerateIvyDescriptor.java:149

     * @since 1.4
     */
    @TaskAction
    public void doGenerate() {
        ivyDescriptorSpec.get().writeTo(getDestinationFile().get().getAsFile());
    }

    IvyDescriptorFileGenerator.DescriptorFileSpec computeIvyDescriptorFileSpec() {
        IvyModuleDescriptorSpecInternal descriptorInternal = toIvyModuleDescriptorInternal(getDescriptor());
        return IvyDescriptorFileGenerator.generateSpec(descriptorInternal);
    }

    private static IvyModuleDescriptorSpecInternal toIvyModuleDescriptorInternal(IvyModuleDescriptorSpec ivyModuleDescriptorSpec) {
        if (ivyModuleDescriptorSpec == null) {
            return null;
        } else if (ivyModuleDescriptorSpec instanceof IvyModuleDescriptorSpecInternal) {
            return (IvyModuleDescriptorSpecInternal) ivyModuleDescriptorSpec;
        } else {
            throw new InvalidUserDataException(
                String.format(
                    "ivyModuleDescriptor implementations must implement the '%s' interface, implementation '%s' does not",
                    IvyModuleDescriptorSpecInternal.class.getName(),
                    ivyModuleDescriptorSpec.getClass().getName()
                )
            );
        }
    }

}

View on GitHub (pinned to 534f27719b)

Solutions

  1. Do not replace the descriptor object - configure the existing one via descriptor.withXml, descriptor.status, descriptor.branch, descriptor.license
  2. If you carry custom values, keep the default descriptor instance and copy the values onto it
  3. Delete code that assigns to the descriptor property of the generate task

Example fix

// before
generateDescriptorFileForIvyPublication.descriptor = new CustomIvyModuleDescriptorSpec()

// after
publishing.publications.ivy.descriptor.withXml {
    asNode().info[0].appendNode('description', 'custom')
}
Defensive patterns

Strategy: type-guard

Type guard

def isGradleManagedDescriptor = { d ->
    d instanceof org.gradle.api.publish.ivy.internal.publication.IvyModuleDescriptorSpecInternal
}
if (!isGradleManagedDescriptor(candidate)) {
    throw new InvalidUserDataException('descriptor must be Gradle-internal; configure the default descriptor instead')
}

Prevention

When it happens

Trigger: tasks.withType(GenerateIvyDescriptor) { descriptor = new MyCustomDescriptorSpec() } where the class implements only org.gradle.api.publish.ivy.IvyModuleDescriptorSpec; proxies or wrappers that recreate the descriptor via the public interface.

Common situations: Build logic copied from old forum posts that constructed custom descriptors; generic proxies (reflection, mapping frameworks) that strip the internal interface; migration from Gradle versions with looser typing.

Related errors


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