hibernate/hibernate-orm · error · IllegalStateException

@Convert placed on @Entity/@MappedSuperclass must define att

Error message

@Convert placed on @Entity/@MappedSuperclass must define attributeName

What it means

A class-level @Convert on an @Entity, @MappedSuperclass, or @Embeddable must state which attribute it converts through attributeName, because Hibernate cannot infer the target attribute from a class placement. ClassPropertyHolder throws this IllegalStateException (note: not AnnotationException) while collecting AttributeConversionInfo when info.getAttributeName() is empty. Field-level @Convert placements do not need attributeName.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/boot/model/internal/ClassPropertyHolder.java:111

			return;
		}

		// collect superclass info first
		collectAttributeConversionInfo( infoMap, entityClassDetails.getSuperClass() );

		final var modelContext = getSourceModelContext();
		final boolean canContainConvert =
				entityClassDetails.hasAnnotationUsage( jakarta.persistence.Entity.class, modelContext )
				|| entityClassDetails.hasAnnotationUsage( jakarta.persistence.MappedSuperclass.class, modelContext )
				|| entityClassDetails.hasAnnotationUsage( jakarta.persistence.Embeddable.class, modelContext );
		if ( ! canContainConvert ) {
			return;
		}

		entityClassDetails.forEachAnnotationUsage( Convert.class, modelContext, (usage) -> {
			final var info = new AttributeConversionInfo( usage, entityClassDetails );
			if ( isEmpty( info.getAttributeName() ) ) {
				throw new IllegalStateException( "@Convert placed on @Entity/@MappedSuperclass must define attributeName" );
			}
			infoMap.put( info.getAttributeName(), info );
		} );
	}

	@Override
	public void startingProperty(MemberDetails property) {
		if ( property != null ) {
			final String propertyName = property.resolveAttributeName();
			if ( !attributeConversionInfoMap.containsKey( propertyName ) ) {
				property.forEachAnnotationUsage( Convert.class, getSourceModelContext(), (usage) -> {
					final var info = new AttributeConversionInfo( usage, property );
					final String infoAttributeName = info.getAttributeName();
					final String path =
							isEmpty( infoAttributeName )
									? propertyName
									: propertyName + '.' + infoAttributeName;
					attributeConversionInfoMap.put( path, info );

View on GitHub (pinned to fad1729dce)

Solutions

  1. Add attributeName: @Convert(converter = OrderStatusConverter.class, attributeName = "status")
  2. Or move the @Convert annotation down onto the target field where attributeName is not needed
  3. For embedded sub-attributes use a dotted path in attributeName, e.g. attributeName = "address.city"

Example fix

// before
@Convert(converter = OrderStatusConverter.class)  // class-level, no attributeName -> error
@Entity
public class Order {
    OrderStatus status;
}

// after
@Convert(converter = OrderStatusConverter.class, attributeName = "status")
@Entity
public class Order {
    OrderStatus status;
}
Defensive patterns

Strategy: validation

Validate before calling

// Fail fast in a test: class-level @Convert must carry attributeName
static void checkClassLevelConverts(Class<?>... entities) {
    for ( Class<?> c : entities ) {
        for ( Convert convert : c.getAnnotationsByType( Convert.class ) ) {
            if ( convert.attributeName().isBlank() ) {
                throw new IllegalStateException( "Class-level @Convert without attributeName on "
                    + c.getName() );
            }
        }
    }
}

Prevention

When it happens

Trigger: Writing @Convert(converter = MyConverter.class) directly on an entity/mapped-superclass/embeddable class without attributeName. The class must carry @Entity, @MappedSuperclass, or @Embeddable for the check to run at all.

Common situations: Moving @Convert from a field to the class to override a converter for an embedded sub-attribute and forgetting attributeName; applying @Converter(autoApply = true) documentation examples that omit attributeName at class level; refactoring converters during a JPA migration.

Related errors


AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22). Data as JSON: /api/errors/fe81abc2144e4f6b. Report an issue: GitHub.