hibernate/hibernate-orm · error · HibernateException
AttributeConverter class [%s] registered multiple times
Error message
AttributeConverter class [%s] registered multiple times
What it means
AttributeConverterManager keeps one descriptor per converter class in a ConcurrentHashMap; addAttributeConverter uses put() and throws HibernateException('AttributeConverter class [X] registered multiple times') whenever a previous entry existed. Any second registration of the same converter class — regardless of whether the descriptor is identical — is rejected.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/boot/model/convert/internal/AttributeConverterManager.java:71
if ( registeredConversionsByDomainType != null ) {
final var domainType = descriptor.getDomainValueResolvedType();
final var registeredConversion = registeredConversionsByDomainType.get( domainType );
if ( registeredConversion != null ) {
// we can skip registering the converter, the RegisteredConversion will always take precedence
if ( BOOT_LOGGER.isDebugEnabled() ) {
BOOT_LOGGER.skippingRegistrationAttributeConverterForAutoApply( converterClass.getName() );
}
return;
}
}
if ( attributeConverterDescriptorsByClass == null ) {
attributeConverterDescriptorsByClass = new ConcurrentHashMap<>();
}
final Object old = attributeConverterDescriptorsByClass.put( converterClass, descriptor );
if ( old != null ) {
throw new HibernateException(
String.format(
Locale.ENGLISH,
"AttributeConverter class [%s] registered multiple times",
converterClass
)
);
}
}
public void addRegistration(RegisteredConversion conversion) {
if ( registeredConversionsByDomainType == null ) {
registeredConversionsByDomainType = new ConcurrentHashMap<>();
}
final var domainType = getDomainType( conversion );
checkNotOverriding( conversion, domainType );
// See if we have a matching entry in attributeConverterDescriptorsByClass.
// If so, remove it. The conversion being registered will always take precedence.View on GitHub (pinned to fad1729dce)
Solutions
- Register each converter class from exactly one place: either rely on @Converter discovery or register programmatically, not both
- Guard programmatic registration with a Set<Class<?>> of already-applied converters (see validation below)
- If multiple builders are fed from one config routine, make it idempotent or re-create it per builder
Example fix
// before
metadataBuilder.applyAttributeConverter(MoneyConverter.class);
// ... later, another module also does:
metadataBuilder.applyAttributeConverter(MoneyConverter.class); // throws
// after
private static final Set<Class<? extends AttributeConverter<?,?>>> REGISTERED = ConcurrentHashMap.newKeySet();
if (REGISTERED.add(MoneyConverter.class)) {
metadataBuilder.applyAttributeConverter(MoneyConverter.class);
} Defensive patterns
Strategy: validation
Validate before calling
private final Set<Class<? extends AttributeConverter<?,?>>> registered = java.util.concurrent.ConcurrentHashMap.newKeySet();
void registerOnce(MetadataBuilder builder, Class<? extends AttributeConverter<?,?>> clazz) {
if (registered.add(clazz)) {
builder.applyAttributeConverter(clazz);
}
// silently skip: class already registered via scan or earlier call
} Try / catch
catch (HibernateException e) {
if (String.valueOf(e.getMessage()).contains("registered multiple times")) {
throw new IllegalStateException("Converter registered twice - deduplicate applyAttributeConverter/scanning paths", e);
}
throw e;
} Prevention
- Choose one registration mechanism: annotations with scanning, or programmatic - not both
- Centralize converter registration in a single bootstrap component
- Make shared bootstrap config idempotent when reused across builders
When it happens
Trigger: Calling MetadataBuilder.applyAttributeConverter(...) for a class that converter auto-discovery/scanning already registered, registering the same class in two configuration code paths (e.g., both a bootstrap hook and the main builder), or duplicated registration inside a loop over packages/entities.
Common situations: Framework integration code registering converters programmatically while @Converter-annotated classes are also scanned; refactors that moved registration into a shared method called twice; tests building several Metadata instances from a shared routine that accumulates registrations.
Related errors
- AttributeConverter class [%s] registered multiple times
- Unable to create AttributeConverter instance
- Duplicate named query '%s'
- Duplicate named stored procedure '{}'
- Duplicate SQL result set mapping '{}'
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/765b3bdb95bfce9d.
Report an issue: GitHub.