hibernate/hibernate-orm · error · HibernateException

SessionFactory configured for multi-tenancy, but no tenant i

Error message

SessionFactory configured for multi-tenancy, but no tenant identifier specified

What it means

Thrown while a Session initializes: the mapping defines tenant discrimination via @TenantId, which Hibernate implements as a session filter (TenantIdBinder.FILTER_NAME), so every session must carry a tenant identifier. setUpMultitenancy() found getTenantIdentifierValue() null, meaning neither the caller (SessionCreationOptions) nor the CurrentTenantIdentifierResolver supplied a tenant. Without it the tenant filter cannot be parameterized and cross-tenant leakage would occur, so Hibernate refuses to start the session.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java:801

	}

	@Override
	@Nonnull
	public SharedSessionBuilder sessionWithOptions() {
		checkSessionReentrancy();
		return new SharedSessionBuilderImpl( this ) {
			@Override
			protected SessionImplementor createSession(SharedStatefulOptions options) {
				return new SessionImpl( factory, options );
			}
		};
	}

	protected final void setUpMultitenancy(SessionFactoryImplementor factory, LoadQueryInfluencers loadQueryInfluencers) {
		if ( factory.getDefinedFilterNames().contains( TenantIdBinder.FILTER_NAME ) ) {
			final Object tenantIdentifier = getTenantIdentifierValue();
			if ( tenantIdentifier == null ) {
				throw new HibernateException( "SessionFactory configured for multi-tenancy, but no tenant identifier specified" );
			}
			else {
				final var resolver = factory.getCurrentTenantIdentifierResolver();
				if ( resolver==null || !resolver.isRoot( tenantIdentifier ) ) {
					// turn on the filter, unless this is the "root" tenant with access to all partitions
					loadQueryInfluencers.enableFilter( TenantIdBinder.FILTER_NAME )
							.setParameter( TenantIdBinder.PARAMETER_NAME, tenantIdentifier );
				}
			}
		}
	}

	private void logInconsistentOptions(SharedSessionCreationOptions sharedOptions) {
		// TODO: these should probable be exceptions!
		if ( sharedOptions.shouldAutoJoinTransactions() ) {
			SESSION_LOGGER.invalidAutoJoinTransactionsWithSharedConnection();
		}
		if ( sharedOptions.getPhysicalConnectionHandlingMode() != connectionHandlingMode ) {

View on GitHub (pinned to fad1729dce)

Solutions

  1. Open every tenant-aware session with an explicit tenant: sessionFactory.withOptions().tenantIdentifier(tenantId).openSession().
  2. Register a CurrentTenantIdentifierResolver (e.g., returning the tenant from a ThreadLocal or SecurityContext) with the SessionFactory/EntityManagerFactory and make sure it never returns null on paths that open sessions.
  3. For JPA bootstrap, pass the tenant at EntityManager creation: emf.createEntityManager(Map.of(AvailableSettings.TENANT_IDENTIFIER, tenantId)).
  4. In jobs and tests, wrap the body with tenantContext.set(...) (or pass the tenant explicitly) so resolver-based resolution always finds a value.

Example fix

// before
try (Session session = sessionFactory.openSession()) { // throws: @TenantId mapped, no tenant
    session.createQuery("from Customer", Customer.class).list();
}
// after
try (Session session = sessionFactory.withOptions().tenantIdentifier(currentTenant()).openSession()) {
    session.createQuery("from Customer", Customer.class).list();
}
Defensive patterns

Strategy: validation

Validate before calling

// Guard every tenant-aware session open behind one helper
public static Session openTenantSession(SessionFactory sf, TenantContext ctx) {
    Object tenant = ctx.get();
    if (tenant == null) {
        throw new IllegalStateException("Tenant context missing; refusing to open session");
    }
    return sf.withOptions().tenantIdentifier(tenant).openSession();
}

Try / catch

try (Session s = sf.openSession()) { ... }
catch (HibernateException e) {
    if (e.getMessage() != null && e.getMessage().contains("no tenant identifier specified")) {
        throw new TenantContextMissingException("Configure tenant before session use", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: An entity carries @TenantId (or the TenantIdBinder filter is otherwise registered) and a session is opened without a tenant: sessionFactory.openSession(), sessionFactory.withOptions().openSession(), or a CurrentTenantIdentifierResolver that returns null for the current context (empty ThreadLocal/SecurityContext, non-web thread).

Common situations: Adding @TenantId to entities in an existing app that opens sessions directly; background jobs, schedulers, and tests running outside the request-scoped tenant context; a Spring tenant-context filter or resolver bean not applied to async/@Scheduled threads; resolver registered too late or not wired into LocalContainerEntityManagerFactoryBean/SessionFactoryBuilder.

Related errors


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