hibernate/hibernate-orm · error · IllegalStateException

Discriminator cannot be de-referenced

Error message

Discriminator cannot be de-referenced

What it means

DiscriminatorSqmPath is the SQM node produced by HQL TYPE(e) / criteria Root.type(). A discriminator holds a scalar class value and is not an attribute-bearing path, so every navigation method on the interface is a default method throwing IllegalStateException('Discriminator cannot be de-referenced'). resolvePathPart is the one hit from the HQL parser when the path continues after type(...), i.e. any attempt to navigate further from the discriminator at query-creation time.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/query/sqm/spi/DiscriminatorSqmPath.java:49

import java.util.Collection;
import java.util.Map;

/**
 * Commonality between entity and any discriminators
 *
 * @author Steve Ebersole
 */
public interface DiscriminatorSqmPath<T> extends SqmPath<T> {
	@Override
	default void appendHqlString(StringBuilder hql, SqmRenderContext context) {
		hql.append( "type(" );
		getLhs().appendHqlString( hql, context );
		hql.append( ')' );
	}

	@Override
	default SqmPath<?> resolvePathPart(String name, boolean isTerminal, SqmCreationState creationState) {
		throw new IllegalStateException( "Discriminator cannot be de-referenced" );
	}

	@Nonnull
	@Override
	default <Y> SqmPath<Y> get(@Nonnull String attributeName) {
		throw new IllegalStateException( "Discriminator cannot be de-referenced" );
	}

	@Nonnull
	@Override
	default <Y> SqmPath<Y> get(@Nonnull SingularAttribute<? super T, Y> attribute) {
		throw new IllegalStateException( "Discriminator cannot be de-referenced" );
	}

	@Nonnull
	@Override
	default <E, C extends Collection<E>> SqmPluralPath<C, E> get(@Nonnull PluralAttribute<? super T, C, E> collection) {
		throw new IllegalStateException( "Discriminator cannot be de-referenced" );

View on GitHub (pinned to fad1729dce)

Solutions

  1. Use the discriminator only where a class literal is legal: type(o) = FullTimeEmployee or type(o) in (...).
  2. To filter by type, bind a Class parameter: ... where type(o) = :type.
  3. Select it directly when you need the value: select type(o) from Order o.
  4. Use TREAT(o AS SubType) when you need subclass state, never type(o).attribute.

Example fix

// before
select o from Order o where type(o).priority = 'HIGH'

// after - compare the discriminator itself, or treat to reach subclass state
select o from Order o where type(o) = RushOrder
select o from Order o where treat(o as RushOrder).priority = 'HIGH'
Defensive patterns

Strategy: type-guard

Validate before calling

// reject 'type(...).' navigation before creating the query
if (hql.matches("(?is).*type\\s*\\([^)]*\\)\\s*\\..*")) {
    throw new IllegalArgumentException("type(...) cannot be de-referenced in: " + hql);
}

Type guard

static boolean isDiscriminator(SqmPath<?> path) {
    return path instanceof org.hibernate.query.sqm.spi.DiscriminatorSqmPath;
}

Try / catch

try {
    return em.createQuery(hql, Order.class).getResultList();
} catch (IllegalStateException e) {
    if (e.getMessage() != null && e.getMessage().contains("Discriminator")) {
        throw new IllegalArgumentException("type() is not navigable - compare it or use TREAT instead", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: HQL such as select type(o).name from Order o or where type(o).id = 1 - the parser resolves the next path part from the discriminator node and this exception is thrown from EntityManager.createQuery(...).

Common situations: Trying to read the entity class name or discriminator column value by navigating it; assuming TYPE(e) behaves like an entity alias; porting native SQL that selects the discriminator column and dereferences it in a subquery.

Related errors


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