hibernate/hibernate-orm · error · HibernateException

Row-level security enabled, but no tenant identifier specifi

Error message

Row-level security enabled, but no tenant identifier specified

What it means

Row-level security is switched on for this factory (SessionFactoryOptions rowLevelSecurityEnabled), so when a session first uses its physical connection setUpRowLevelSecurity() must bind the tenant onto it (RowLevelSecurity.setTenantIdentifier or a dedicated DB user in user-mode). getTenantIdentifierValue() returned null, so the connection cannot be pinned to a tenant partition and Hibernate aborts rather than run with unrestricted row visibility.

Source

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

	public void afterTransactionBegin() {
		setUpRowLevelSecurity();
	}

	private RowLevelSecurity getRowLevelSecurity() {
		return getJdbcServices().getDialect().getRowLevelSecurity();
	}

	private void setUpRowLevelSecurity() {
		if ( rowLevelSecurityEnabled ) {
			setUpRowLevelSecurity( getJdbcCoordinator().getLogicalConnection().getPhysicalConnection() );
		}
	}

	private void setUpRowLevelSecurity(Connection connection) {
		if ( rowLevelSecurityEnabled ) {
			final Object tenantIdentifier = getTenantIdentifierValue();
			if ( tenantIdentifier == null ) {
				throw new HibernateException( "Row-level security enabled, but no tenant identifier specified" );
			}

			if ( !useDatabaseUserForRowLevelSecurity() ) {
				final var resolver = factory.getCurrentTenantIdentifierResolver();
				final boolean root = resolver != null && resolver.isRoot( tenantIdentifier );
				try {
					getRowLevelSecurity()
							.setTenantIdentifier( connection, getTenantIdentifier(), root );
				}
				catch (SQLException e) {
					throw getJdbcServices().getSqlExceptionHelper()
							.convert( e, "Unable to set row-level security tenant identifier" );
				}
			}
		}
	}

	private boolean useDatabaseUserForRowLevelSecurity() {

View on GitHub (pinned to fad1729dce)

Solutions

  1. Provide the tenant for every session exactly as for multi-tenancy: withOptions().tenantIdentifier(...) on open, or a CurrentTenantIdentifierResolver that always resolves a value (see error 1540).
  2. If row-level security was enabled by accident or is not needed for this factory, remove the setting.
  3. In database-user mode, also verify the tenant-to-user mapping exists so useDatabaseUserForRowLevelSecurity() can switch users.

Example fix

// before
Session s = sf.openSession(); // row-level security enabled, no tenant
s.find(Customer.class, 1L); // HibernateException
// after
Session s = sf.withOptions().tenantIdentifier(tenantContext.get()).openSession();
Defensive patterns

Strategy: validation

Validate before calling

Object tenant = tenantContext.get();
if (tenant == null) {
    throw new IllegalStateException("Row-level security requires a tenant on every session");
}
try (Session s = sf.withOptions().tenantIdentifier(tenant).openSession()) { ... }

Prevention

When it happens

Trigger: Enabling the row-level security setting (e.g., hibernate.row_level_security) while opening sessions/EntityManagers without a tenant identifier and without a CurrentTenantIdentifierResolver that yields one; typically fails on the first statement that acquires the physical connection.

Common situations: Adopting Hibernate 7 row-level security (PostgreSQL/Oracle RLS integration) without porting over the tenant-context infrastructure; system/background sessions that have no tenant; resolver wired only for web request threads.

Related errors


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