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

  1. Scope sessions to the tenant: close/unbind the old session when the tenant changes so a fresh one is created
  2. Use a SessionFactory per tenant (or per-tenant session maps) when switching is frequent — cleaner than validation errors
  3. Fix the caller: check why the tenant context changed mid-session (authentication/routing bug, leaked ThreadLocal)
  4. 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

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


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