{"record":{"id":"e66ff65b5d2c7a5e","repo":"hibernate/hibernate-orm","slug":"conflicting-converterregistration-descriptors-f","errorCode":null,"errorMessage":"Conflicting '@ConverterRegistration' descriptors for attribute converter '${converterTypeName}'","messagePattern":"Conflicting '@ConverterRegistration' descriptors for attribute converter '(.+?)'","errorType":"exception","errorClass":"AnnotationException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/boot/model/convert/internal/AttributeConverterManager.java","lineNumber":106,"sourceCode":"\t\t// See if we have a matching entry in attributeConverterDescriptorsByClass.\n\t\t// If so, remove it. The conversion being registered will always take precedence.\n\t\tif ( attributeConverterDescriptorsByClass != null ) {\n\t\t\tfinal var removed = attributeConverterDescriptorsByClass.remove( conversion.getConverterType() );\n\t\t\tif ( removed != null && BOOT_LOGGER.isDebugEnabled() ) {\n\t\t\t\tBOOT_LOGGER.removedPotentiallyAutoApplicableConverterDueToRegistration(\n\t\t\t\t\t\tremoved.getAttributeConverterClass().getName() );\n\t\t\t}\n\t\t}\n\t\tregisteredConversionsByDomainType.put( domainType, conversion );\n\t}\n\n\tprivate void checkNotOverriding(RegisteredConversion conversion, Type domainType) {\n\t\t// make sure we are not overriding a previous conversion registration\n\t\tfinal var existingRegistration = registeredConversionsByDomainType.get( domainType );\n\t\tif ( existingRegistration != null ) {\n\t\t\tfinal String converterTypeName = conversion.getConverterType().getName();\n\t\t\tif ( !conversion.equals( existingRegistration ) ) {\n\t\t\t\tthrow new AnnotationException( \"Conflicting '@ConverterRegistration' descriptors for attribute converter '\"\n\t\t\t\t\t\t\t\t\t\t\t\t+ converterTypeName + \"'\" );\n\t\t\t}\n\t\t\telse {\n\t\t\t\tBOOT_LOGGER.skippingDuplicateConverterRegistration( converterTypeName );\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static Type getDomainType(RegisteredConversion conversion) {\n\t\t// the registration did not define an explicit domain-type, so inspect the converter\n\t\treturn conversion.getExplicitDomainType().equals( void.class )\n\t\t\t\t? typeArguments( AttributeConverter.class, conversion.getConverterType() )[0]\n\t\t\t\t: conversion.getExplicitDomainType();\n\t}\n\n\tprivate Collection<ConverterDescriptor<?,?>> converterDescriptors() {\n\t\treturn attributeConverterDescriptorsByClass == null\n\t\t\t\t? emptyList()","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/boot/model/convert/internal/AttributeConverterManager.java#L88-L124","documentation":"AttributeConverterManager.checkNotOverriding allows only one RegisteredConversion per domain type: if a registration already exists and the new one is not equal to it, Hibernate throws AnnotationException(\"Conflicting '@ConverterRegistration' descriptors for attribute converter 'X'\"). Equal duplicates are just logged and skipped, so the error specifically means two different registrations compete for the same domain type.","triggerScenarios":"Two @ConverterRegistration annotations (typically on package-info.java, possibly across merged modules) whose domainType resolves to the same type but whose converter or settings differ — e.g. domainType=Duration with converter=DurationToStringConverter in one module and DurationLongConverter in another, or the same converter registered twice with different autoApply/override flags.","commonSituations":"Modular codebases where several modules register converters for shared JDK types (java.time, enums, UUID); merging projects that each carried their own converter registrations; editing a registration while forgetting a copy elsewhere.","solutions":["Consolidate to exactly one @ConverterRegistration per domain type — search the whole project for '@ConverterRegistration' and the conflicting domain type","If different parts of the app need different converters for the same type, disable auto-apply registration for one and apply it explicitly with @Convert on the specific attributes","Keep registrations centralized in a single package-info (e.g. the root or a converters package) so conflicts are visible in one file"],"exampleFix":"// before (two modules, each package-info.java)\n@ConverterRegistration(domainType = Duration.class, converter = DurationLongConverter.class)\n@ConverterRegistration(domainType = Duration.class, converter = DurationStringConverter.class)\n\n// after: one auto-apply registration; the other applied per-attribute\n// package-info.java\n@ConverterRegistration(domainType = Duration.class, converter = DurationLongConverter.class)\n// entity field needing the other one:\n@Convert(converter = DurationStringConverter.class)\nprivate Duration reportingPeriod;","handlingStrategy":"validation","validationCode":"// scan all package-info @ConverterRegistrations and assert one domain type each:\nMap<Class<?>, String> byDomain = new HashMap<>();\nfor (Package p : packages) {\n    ConverterRegistrations regs = p.getAnnotation(ConverterRegistrations.class);\n    if (regs == null) continue;\n    for (ConverterRegistration r : regs.value()) {\n        String prev = byDomain.put(r.domainType(), r.converter().getName());\n        if (prev != null && !prev.equals(r.converter().getName())) {\n            throw new IllegalStateException(\"Two @ConverterRegistration for \" + r.domainType() + \": \" + prev + \" vs \" + r.converter().getName());\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":"catch (AnnotationException e) {\n    if (String.valueOf(e.getMessage()).contains(\"Conflicting '@ConverterRegistration'\")) {\n        throw new IllegalStateException(\"Deduplicate @ConverterRegistration for the domain type named in the message\", e);\n    }\n    throw e;\n}","preventionTips":["Keep all @ConverterRegistration declarations in one package-info for the whole application","When merging modules, grep for duplicate domainType registrations first","Use @Convert for per-attribute exceptions instead of a second registration"],"tags":["jpa","attribute-converter","converter-registration","annotations"],"backgroundTag":"conflicting-converter-registration","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}