hibernate/hibernate-orm · critical · ServiceException

A bean manager was provided via 'jakarta.persistence.bean.ma

Error message

A bean manager was provided via 'jakarta.persistence.bean.manager' but CDI is not available on the Hibernate class loader

What it means

When a BeanManager reference is supplied via 'jakarta.persistence.bean.manager', Hibernate first verifies that the CDI API itself is loadable through its own ClassLoaderService (isCdiAvailable). If jakarta.enterprise.inject.spi.BeanManager cannot be loaded from Hibernate's class loader, it refuses to use the reference and throws ServiceException at bootstrap.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/resource/beans/spi/ManagedBeanRegistryInitiator.java:80

	private static BeanContainer interpretImplicitBeanContainer(ServiceRegistry serviceRegistry) {
		final Collection<BeanContainer> beanContainers =
				serviceRegistry.requireService( ClassLoaderService.class )
						.loadJavaServices( BeanContainer.class );
		return switch ( beanContainers.size() ) {
			case 1 -> beanContainers.iterator().next();
			case 0 -> interpretImplicitCdiBeanContainer( serviceRegistry );
			default -> throw new ServiceException( "Multiple BeanContainer service implementations found"
													+ " (set '" + BEAN_CONTAINER + "' explicitly)" );
		};
	}

	// simplified CDI support
	private static BeanContainer interpretImplicitCdiBeanContainer(ServiceRegistry serviceRegistry) {
		final Object beanManager = getConfiguredBeanManager( serviceRegistry );
		final boolean isCdiAvailable = isCdiAvailable( serviceRegistry );
		if ( beanManager != null ) {
			if ( !isCdiAvailable ) {
				throw new ServiceException( "A bean manager was provided via '" + JAKARTA_CDI_BEAN_MANAGER
											+ "' but CDI is not available on the Hibernate class loader");
			}
			return CdiBeanContainerBuilder.fromBeanManagerReference( beanManager, serviceRegistry );
		}
		else {
			if ( isCdiAvailable ) {
				BEANS_MSG_LOGGER.noBeanManagerButCdiAvailable();
			}
			return null;
		}
	}

	private static Object getConfiguredBeanManager(ServiceRegistry serviceRegistry) {
		final Map<String, Object> settings = serviceRegistry.requireService( ConfigurationService.class ).getSettings();
		final Object beanManager = settings.get( JAKARTA_CDI_BEAN_MANAGER );
		return beanManager != null ? beanManager : settings.get( CDI_BEAN_MANAGER );
	}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Add jakarta.enterprise:jakarta.enterprise.cdi-api (and an implementation if beans are used) to the same classpath/classloader as hibernate-core.
  2. In modular setups, ensure Hibernate's module reads the CDI API module (requires static jakarta.enterprise.cdi).
  3. Remove the 'jakarta.persistence.bean.manager' property when CDI is not actually in use.
  4. Verify with Class.forName("jakarta.enterprise.inject.spi.BeanManager") on Hibernate's classloader during a startup check.

Example fix

// before
Map<String, Object> props = Map.of("jakarta.persistence.bean.manager", weldBeanManager);
// hibernate-core cannot load jakarta.enterprise.inject.spi.BeanManager -> ServiceException

// after - put the CDI API next to hibernate-core (Maven)
<dependency>
  <groupId>jakarta.enterprise</groupId>
  <artifactId>jakarta.enterprise.cdi-api</artifactId>
</dependency>
// or drop the property entirely when CDI is not used
Defensive patterns

Strategy: validation

Validate before calling

// only pass a bean manager if Hibernate can see the CDI API on its classloader
try {
    Class.forName("jakarta.enterprise.inject.spi.BeanManager", false,
        org.hibernate.Internal.class.getClassLoader());
} catch (ClassNotFoundException e) {
    props.remove("jakarta.persistence.bean.manager"); // or fix the classpath first
}

Try / catch

try {
    emf = Persistence.createEntityManagerFactory("pu", props);
} catch (org.hibernate.service.spi.ServiceException e) {
    if (e.getMessage().contains("CDI is not available")) {
        // add cdi-api next to hibernate-core, or remove the bean.manager property
        throw new ConfigurationException(e.getMessage(), e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Passing a BeanManager while hibernate-core cannot see the CDI API: cdi-api missing from the application classpath, a module-path/layered classloading setup where Hibernate's class loader does not read the CDI module, or the bean manager object coming from a different classloader than Hibernate.

Common situations: Shaded/modular applications that omit jakarta.enterprise.cdi-api; app-server deployments with visibility restrictions on Hibernate's class loader; SE apps where the property is set globally but CDI is not actually used.

Related errors


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