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
- Do not replace the descriptor object - configure the existing one via descriptor.withXml, descriptor.status, descriptor.branch, descriptor.license
- If you carry custom values, keep the default descriptor instance and copy the values onto it
- 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
- Never assign custom objects to the descriptor property of GenerateIvyDescriptor
- Customize descriptors via descriptor.withXml and its properties
- Treat internal interfaces (IvyModuleDescriptorSpecInternal) as Gradle implementation details
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
- publication objects must implement the '%s' interface, imple
- Cannot create DslObject for '%s' (class: %s) as it does not
- Unsupported pattern '%s'
- supplied %s does not match ivy descriptor (cannot edit %1$s
- publication objects must implement the '%s' interface, imple
AI-assisted analysis of gradle/gradle@534f27719b (2026-08-22).
Data as JSON: /api/errors/d4d22b6bc176ff13.
Report an issue: GitHub.