hibernate/hibernate-orm · error · UnsupportedOperationException
CTE joins can not be treated
Error message
CTE joins can not be treated
What it means
SqmCteJoin is Hibernate's SQM (semantic query model) node for joining a CTE declared with `with c as (...)`, built by HQL `join c on ...` or criteria `JpaFrom.join(JpaCteCriteria)`. TREAT downcasts a join to an entity subtype, which requires a polymorphic persistent entity type behind the join; a CTE is an anonymous query-result shape with no metamodel inheritance (SqmCteJoin.getAttribute() returns null). Therefore every treatAs overload on SqmCteJoin fails fast with UnsupportedOperationException the moment it is invoked during query construction, before any SQL is generated.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/query/sqm/tree/spi/from/SqmCteJoin.java:142
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// JPA
@Override
@Nonnull
public SqmCorrelatedCteJoin<T> createCorrelation() {
return new SqmCorrelatedCteJoin<>( this );
}
@Nullable
@Override
public PersistentAttribute<? super T, ?> getAttribute() {
return null;
}
@Override
@Nonnull
public <S extends T> SqmTreatedJoin<T, T, S> treatAs(@Nonnull Class<S> treatJavaType, @Nullable String alias) {
throw new UnsupportedOperationException( "CTE joins can not be treated" );
}
@Override
@Nonnull
public <S extends T> SqmTreatedJoin<T, T, S> treatAs(@Nonnull EntityDomainType<S> treatTarget, @Nullable String alias) {
throw new UnsupportedOperationException( "CTE joins can not be treated" );
}
@Override
@Nonnull
public <S extends T> SqmTreatedJoin<T, T, S> treatAs(@Nonnull Class<S> treatJavaType, @Nullable String alias, boolean fetched) {
throw new UnsupportedOperationException( "CTE joins can not be treated" );
}
@Override
@Nonnull
public <S extends T> SqmTreatedJoin<T, T, S> treatAs(@Nonnull EntityDomainType<S> treatTarget, @Nullable String alias, boolean fetched) {
throw new UnsupportedOperationException( "CTE joins can not be treated" );View on GitHub (pinned to fad1729dce)
Solutions
- Remove the treat from the CTE join and constrain the subtype inside the CTE body: declare the CTE over the subtype (`with emp as (select e from Employee e ...)`) or filter there with `where type(p) = SubType`.
- Apply TREAT to the real polymorphic entity join elsewhere in the query (attribute joins support treat) and have the CTE select from that treated root instead of treating the CTE join itself.
- If only subtype columns are needed, project them as plain columns in the CTE and drop the downcast at the join site.
Example fix
-- before: parser calls SqmCteJoin.treatAs(Class, String) -> UnsupportedOperationException with people as (select p from Person p) select t.salary from Client c join people p on p.id = c.id join treat(p as Employee) t on t.id = p.id -- after: constrain the subtype inside the CTE; no treat on the CTE join with employees as (select e from Employee e) select e.salary from Client c join employees e on e.id = c.id
Defensive patterns
Strategy: type-guard
Validate before calling
// before applying any treat, verify the join actually models an entity type
import org.hibernate.query.sqm.tree.spi.from.*;
if ( join instanceof SqmCteJoin<?> ) {
throw new IllegalArgumentException(
"TREAT is unsupported on CTE joins; constrain the subtype inside the CTE definition" );
} Type guard
import org.hibernate.query.sqm.tree.spi.from.*;
static boolean supportsTreatAs(Join<?, ?> join) {
// CTE/derived/function joins are anonymous query shapes: no treat
return !( join instanceof SqmCteJoin<?> )
&& !( join instanceof SqmDerivedJoin<?> )
&& !( join instanceof SqmFunctionJoin<?> );
} Try / catch
try {
JpaTreatedJoin<?, ?, ?> treated = ( (JpaJoin<?, ?>) join ).treatAs( Sub.class );
} catch ( UnsupportedOperationException e ) {
if ( e.getMessage() != null && e.getMessage().contains( "can not be treated" ) ) {
// query-shape bug, not transient: reject with context, never retry
throw new IllegalArgumentException( "TREAT unsupported on join " + join.getAlias(), e );
}
throw e;
} Prevention
- Only call treatAs on joins backed by a polymorphic entity (attribute joins, roots); CTE/derived/function joins have no type to downcast.
- Push subtype restrictions into the CTE body: select from the subtype or filter with type(p) = SubType.
- In dynamic query builders, branch on the concrete SQM join class before applying TREAT.
- Add one integration test per query shape when introducing CTE joins so treat regressions fail in CI, not production.
When it happens
Trigger: Calling treatAs(Class<S> treatJavaType, String alias) on a join whose SQM node is a SqmCteJoin: HQL `join treat(cteAlias as SubType) t on ...` (the HQL translator invokes the aliased overload), or SPI/criteria-translation code that holds the JpaJoin returned by root.join(JpaCteCriteria) and applies an aliased treat.
Common situations: Refactoring a polymorphic entity join into a CTE-based reporting query while keeping a TREAT call; generic query-DSL helpers that uniformly call treatAs(type, alias) on any Join; upgrading to Hibernate 7 where SQM join classes moved under org.hibernate.query.sqm.tree.spi.from and CTE joins now route to SqmCteJoin.
Related errors
- Cte root does not have an entity type. Use getReferencedPath
- Derived joins can not be treated
- Entity join treats can not be aliased
- Function joins can not be treated
- MappedSuperclassType cannot be used to create an SqmPath - t
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/f47bebbd8539bd85.
Report an issue: GitHub.