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
- Open every tenant-aware session with an explicit tenant: sessionFactory.withOptions().tenantIdentifier(tenantId).openSession().
- 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.
- For JPA bootstrap, pass the tenant at EntityManager creation: emf.createEntityManager(Map.of(AvailableSettings.TENANT_IDENTIFIER, tenantId)).
- 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
- Route all session opening through one factory method that injects the tenant
- Make CurrentTenantIdentifierResolver throw a descriptive error itself when context is empty instead of returning null
- Set up tenant context in schedulers, async executors, and test setups, not just web filters
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
- all @TenantId fields must have the same type: <parameterType
- @TenantId attribute must be mapped to a single column or for
- assigned tenant id differs from current tenant id [{} != {}]
- Row-level security enabled, but no tenant identifier specifi
- Updating immutable entity that is not in session yet
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/7c1a531be3498883.
Report an issue: GitHub.