hibernate/hibernate-orm · error · IllegalArgumentException

Named type [${typeImplementorClass}] did not implement Basic

Error message

Named type [${typeImplementorClass}] did not implement BasicType nor UserType

What it means

When a named type (from hbm.xml <typedef>/<type name=.../>) resolves to a class that is not a BasicType/UserType implementation, TypeDefinition falls back to 'legacy' adaptation: Serializable classes become SerializableType, interfaces become JavaObjectType, and everything else throws IllegalArgumentException('Named type [X] did not implement BasicType nor UserType'). Plain non-serializable classes cannot be adapted into a Hibernate basic type.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/boot/model/TypeDefinition.java:246

		// Series of backward compatible special cases
		return resolveLegacyCases( typeImplementorClass, indicators, typeConfiguration );
	}

	private static <T> BasicValue.Resolution<T> resolveLegacyCases(
			Class<T> typeImplementorClass, JdbcTypeIndicators indicators, TypeConfiguration typeConfiguration) {
		return createBasicTypeResolution( getLegacyType( typeImplementorClass ),
				typeImplementorClass, indicators, typeConfiguration );
	}

	private static <T> BasicType<T> getLegacyType(Class<T> typeImplementorClass) {
		if ( Serializable.class.isAssignableFrom( typeImplementorClass ) ) {
			return new SerializableType( typeImplementorClass );
		}
		else if ( typeImplementorClass.isInterface() ) {
			return (BasicType<T>) new JavaObjectType();
		}
		else {
			throw new IllegalArgumentException( "Named type [" + typeImplementorClass
												+ "] did not implement BasicType nor UserType" );
		}
	}

	private static <T> BasicValue.Resolution<T> createBasicTypeResolution(
			BasicType<T> type,
			Class<T> typeImplementorClass,
			JdbcTypeIndicators indicators,
			TypeConfiguration typeConfiguration) {
		final var jtd = typeConfiguration.getJavaTypeRegistry().resolveDescriptor( typeImplementorClass );
		final var jdbcType = typeConfiguration.getJdbcTypeRegistry().getDescriptor( Types.VARBINARY );
		final var basicType = typeConfiguration.getBasicTypeRegistry().resolve( jtd, jdbcType );
		final var resolved = resolveSqlTypeIndicators( indicators, basicType, jtd );

		return new BasicValue.Resolution<>() {
			@Override
			public JdbcMapping getJdbcMapping() {
				return resolved;

View on GitHub (pinned to fad1729dce)

Solutions

  1. Write a custom type implementing org.hibernate.usertype.UserType (or CompositeUserType) for the value class and reference that type's class name in the mapping
  2. Alternatively make the value class implement java.io.Serializable so the legacy SerializableType adaptation applies (stores as serialized binary — usually the worse option)
  3. Verify the FQN in the mapping is the type class, not the domain value class, and that it exists on the classpath

Example fix

<!-- before -->
<typedef name="money" class="com.acme.Money"/> <!-- plain value class -->

<!-- after -->
<typedef name="money" class="com.acme.MoneyType"/> <!-- implements UserType -->
Defensive patterns

Strategy: type-guard

Type guard

static boolean isAdaptableAsHibernateType(Class<?> c) {
    return org.hibernate.type.BasicType.class.isAssignableFrom(c)
        || org.hibernate.usertype.UserType.class.isAssignableFrom(c)
        || org.hibernate.usertype.CompositeUserType.class.isAssignableFrom(c)
        || java.io.Serializable.class.isAssignableFrom(c)
        || c.isInterface();
}

Try / catch

catch (IllegalArgumentException e) {
    if (String.valueOf(e.getMessage()).contains("did not implement BasicType nor UserType")) {
        throw new IllegalStateException("Referenced type class must implement UserType/BasicType - fix the typedef class", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: A <typedef class="com.acme.Money"/> or @Type(...)/hbm <type name="..."> pointing at a class that implements none of Hibernate's type interfaces and is neither Serializable nor an interface — most often the value class itself instead of a UserType that wraps it, or a typo that resolves to some other plain class.

Common situations: Mapping a custom value object directly by class name instead of writing a UserType; classpaths where the intended UserType class was renamed so the FQN now hits an unrelated class; legacy hbm.xml brought forward from very old Hibernate versions.

Related errors


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