hibernate/hibernate-orm · error · BeanIntrospectionException

Bean [" + bean + "] was not of declared bean type [" + beanC

Error message

Bean [" + bean + "] was not of declared bean type [" + beanClass.getName() + "]

What it means

BeanInfoHelper caches a BeanInfo for a declared beanClass; applyToBeanInfo(bean, delegate) first asserts the concrete bean really is an instance of that class via beanClass.isInstance(bean). When the check fails it throws BeanIntrospectionException. Because isInstance is a runtime-identity check, two copies of the same class name loaded by different classloaders also fail it despite identical names.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/internal/util/beans/BeanInfoHelper.java:71

	public interface ReturningBeanInfoDelegate<T> {
		T processBeanInfo(BeanInfo beanInfo) throws Exception;
	}

	private final Class<?> beanClass;
	private final Class<?> stopClass;

	public BeanInfoHelper(Class<?> beanClass) {
		this( beanClass, Object.class );
	}

	public BeanInfoHelper(Class<?> beanClass, Class<?> stopClass) {
		this.beanClass = beanClass;
		this.stopClass = stopClass;
	}

	public void applyToBeanInfo(Object bean, BeanInfoDelegate delegate) {
		if ( ! beanClass.isInstance( bean ) ) {
			throw new BeanIntrospectionException( "Bean [" + bean + "] was not of declared bean type [" + beanClass.getName() + "]" );
		}

		visitBeanInfo( beanClass, stopClass, delegate );
	}

	public static void visitBeanInfo(Class<?> beanClass, BeanInfoDelegate delegate) {
		visitBeanInfo( beanClass, Object.class, delegate );
	}

	public static void visitBeanInfo(Class<?> beanClass, Class<?> stopClass, BeanInfoDelegate delegate) {
		try {
			final BeanInfo info = getBeanInfo( beanClass, stopClass );
			try {
				delegate.processBeanInfo( info );
			}
			catch ( RuntimeException e ) {
				throw e;
			}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Pass a bean whose runtime type is beanClass or a subclass of it.
  2. On classloader mismatches, deserialize or load with beanClass's own classloader (or the TCCL) so both sides share one class identity.
  3. Add an instanceof assertion at your boundary with a clear message instead of letting introspection fail.

Example fix

// before
BeanInfoHelper helper = new BeanInfoHelper(Customer.class);
helper.applyToBeanInfo(someMap, info -> { }); // Map is not a Customer -> BeanIntrospectionException

// after
if (!(someObject instanceof Customer c)) throw new IllegalArgumentException("expected Customer");
helper.applyToBeanInfo(c, info -> { });
Defensive patterns

Strategy: type-guard

Validate before calling

if (!(beanClass.isInstance(bean))) {
    throw new IllegalArgumentException("expected instance of " + beanClass.getName() + " but got " + bean.getClass().getName());
}
helper.applyToBeanInfo(bean, delegate);

Type guard

static boolean isInstanceOfClass(Class<?> beanClass, Object bean) {
    return bean != null && beanClass.isInstance(bean);
}

Try / catch

try {
    helper.applyToBeanInfo(bean, delegate);
} catch (org.hibernate.internal.util.beans.BeanIntrospectionException e) {
    // type mismatch: compare bean.getClass().getName() vs beanClass.getName(); equal names -> classloader duplication
}

Prevention

When it happens

Trigger: Calling applyToBeanInfo with a bean of an unrelated runtime type (e.g., a Map or a DTO where the entity class was declared), or with an object deserialized or loaded under a different classloader than the one that loaded beanClass.

Common situations: App-server redeploys and parent/child classloader splits; objects deserialized with the wrong loader; generic plumbing that loses the static type and passes Object; test fixtures reusing helpers with mismatched bean classes.

Related errors


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