hibernate/hibernate-orm · error · TenantIdentifierMismatchException
Reported current tenant identifier [%s] did not match tenant
Error message
Reported current tenant identifier [%s] did not match tenant identifier from existing session [%s]
What it means
Same validation as the null-mismatch variant, but here both the resolver's current tenant and the bound session's tenant are non-null and differ according to tenantIdentifierJavaType.areEqual(current, session). Hibernate throws TenantIdentifierMismatchException (a subclass of HibernateException) rather than silently handing out a session scoped to a different tenant.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/context/spi/AbstractCurrentSessionContext.java:56
}
return builder;
}
protected void validateExistingSession(Session existingSession) {
final var resolver = factory.getCurrentTenantIdentifierResolver();
if ( resolver != null && resolver.validateExistingCurrentSessions() ) {
final Object currentValue = resolver.resolveCurrentTenantIdentifier();
final var tenantIdentifierJavaType = factory.getTenantIdentifierJavaType();
final Object tenantIdentifierValue = existingSession.getTenantIdentifierValue();
if ( tenantIdentifierValue == null || currentValue == null ) {
if ( tenantIdentifierValue != currentValue ) {
throw new TenantIdentifierMismatchException(
"Reported current tenant identifier did not match tenant identifier from existing session [%s]"
);
}
}
else if ( !tenantIdentifierJavaType.areEqual( currentValue, tenantIdentifierValue ) ) {
throw new TenantIdentifierMismatchException(
"Reported current tenant identifier [%s] did not match tenant identifier from existing session [%s]"
.formatted( tenantIdentifierJavaType.toString( currentValue ),
tenantIdentifierJavaType.toString( tenantIdentifierValue ) )
);
}
}
}
}
View on GitHub (pinned to fad1729dce)
Solutions
- Scope sessions to the tenant: close/unbind the old session when the tenant changes so a fresh one is created
- Use a SessionFactory per tenant (or per-tenant session maps) when switching is frequent — cleaner than validation errors
- Fix the caller: check why the tenant context changed mid-session (authentication/routing bug, leaked ThreadLocal)
- Keep validateExistingCurrentSessions() = true in production; it is the guard against cross-tenant data leaks
Example fix
// before
// thread still bound to tenant A's session; request is for tenant B
tenantHolder.set("B");
Session s = sessionFactory.getCurrentSession(); // throws TenantIdentifierMismatchException
// after
if (ManagedSessionContext.hasBind(sessionFactory)) {
Session old = (Session) ManagedSessionContext.unbind(sessionFactory);
old.close(); // drop tenant A's session first
}
tenantHolder.set("B");
Session s = sessionFactory.getCurrentSession(); // fresh session for tenant B Defensive patterns
Strategy: try-catch
Validate before calling
String current = tenantResolver.resolveCurrentTenantIdentifier();
if (sessionFactory.getCurrentSession().isTransactionInProgress()
&& !Objects.equals(current, expectedTenantOfBoundSession)) {
unbindAndCloseCurrentSession(); // avoid mismatch before it throws
} Try / catch
try {
return sessionFactory.getCurrentSession();
} catch (org.hibernate.context.TenantIdentifierMismatchException e) {
Session old = (Session) org.hibernate.context.internal.ManagedSessionContext.unbind(sessionFactory);
if (old != null) old.close();
return sessionFactory.getCurrentSession(); // fresh session for the new tenant
} Prevention
- Close/unbind sessions whenever the tenant changes; never reuse across tenants
- Prefer per-tenant SessionFactory or per-tenant session maps for frequent switching
- Keep validateExistingCurrentSessions() = true in production — it is the cross-tenant leak guard
- Investigate routing/auth bugs whenever this fires; do not just catch it
When it happens
Trigger: A real tenant switch hits an existing session: request for tenant B runs on a context still holding the session opened for tenant A — e.g. pooled/cached sessions, missing unbind on tenant change, or a shared current-session context across tenant-specific dispatch code.
Common situations: Multi-tenant web tiers where the tenant is resolved per request but the session context outlives it; background jobs iterating tenants while reusing getCurrentSession(); frontend bugs sending tenant A's token with tenant B's path; test suites switching tenants without resetting session bindings.
Related errors
- Reported current tenant identifier did not match tenant iden
- Could not obtain TransactionManager from JtaPlatform
- Unable to register cleanup Synchronization with TransactionM
- Unable to locate current JTA transaction
- Current transaction is not in progress
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/876fc4a2dc257920.
Report an issue: GitHub.