{"record":{"id":"f47bebbd8539bd85","repo":"hibernate/hibernate-orm","slug":"cte-joins-can-not-be-treated","errorCode":null,"errorMessage":"CTE joins can not be treated","messagePattern":"CTE joins can not be treated","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/query/sqm/tree/spi/from/SqmCteJoin.java","lineNumber":142,"sourceCode":"\t// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\t// JPA\n\n\t@Override\n\t@Nonnull\n\tpublic SqmCorrelatedCteJoin<T> createCorrelation() {\n\t\treturn new SqmCorrelatedCteJoin<>( this );\n\t}\n\n\t@Nullable\n\t@Override\n\tpublic PersistentAttribute<? super T, ?> getAttribute() {\n\t\treturn null;\n\t}\n\n\t@Override\n\t@Nonnull\n\tpublic <S extends T> SqmTreatedJoin<T, T, S> treatAs(@Nonnull Class<S> treatJavaType, @Nullable String alias) {\n\t\tthrow new UnsupportedOperationException( \"CTE joins can not be treated\" );\n\t}\n\n\t@Override\n\t@Nonnull\n\tpublic <S extends T> SqmTreatedJoin<T, T, S> treatAs(@Nonnull EntityDomainType<S> treatTarget, @Nullable String alias) {\n\t\tthrow new UnsupportedOperationException( \"CTE joins can not be treated\" );\n\t}\n\n\t@Override\n\t@Nonnull\n\tpublic <S extends T> SqmTreatedJoin<T, T, S> treatAs(@Nonnull Class<S> treatJavaType, @Nullable String alias, boolean fetched) {\n\t\tthrow new UnsupportedOperationException( \"CTE joins can not be treated\" );\n\t}\n\n\t@Override\n\t@Nonnull\n\tpublic <S extends T> SqmTreatedJoin<T, T, S> treatAs(@Nonnull EntityDomainType<S> treatTarget, @Nullable String alias, boolean fetched) {\n\t\tthrow new UnsupportedOperationException( \"CTE joins can not be treated\" );","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/spi/from/SqmCteJoin.java#L124-L160","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"-- before: parser calls SqmCteJoin.treatAs(Class, String) -> UnsupportedOperationException\nwith people as (select p from Person p)\nselect t.salary from Client c\n  join people p on p.id = c.id\n  join treat(p as Employee) t on t.id = p.id\n\n-- after: constrain the subtype inside the CTE; no treat on the CTE join\nwith employees as (select e from Employee e)\nselect e.salary from Client c\n  join employees e on e.id = c.id","handlingStrategy":"type-guard","validationCode":"// before applying any treat, verify the join actually models an entity type\nimport org.hibernate.query.sqm.tree.spi.from.*;\n\nif ( join instanceof SqmCteJoin<?> ) {\n    throw new IllegalArgumentException(\n        \"TREAT is unsupported on CTE joins; constrain the subtype inside the CTE definition\" );\n}","typeGuard":"import org.hibernate.query.sqm.tree.spi.from.*;\n\nstatic boolean supportsTreatAs(Join<?, ?> join) {\n    // CTE/derived/function joins are anonymous query shapes: no treat\n    return !( join instanceof SqmCteJoin<?> )\n        && !( join instanceof SqmDerivedJoin<?> )\n        && !( join instanceof SqmFunctionJoin<?> );\n}","tryCatchPattern":"try {\n    JpaTreatedJoin<?, ?, ?> treated = ( (JpaJoin<?, ?>) join ).treatAs( Sub.class );\n} catch ( UnsupportedOperationException e ) {\n    if ( e.getMessage() != null && e.getMessage().contains( \"can not be treated\" ) ) {\n        // query-shape bug, not transient: reject with context, never retry\n        throw new IllegalArgumentException( \"TREAT unsupported on join \" + join.getAlias(), e );\n    }\n    throw e;\n}","preventionTips":["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."],"tags":["hibernate","hql","jpa","criteria-api","cte","treat-as","sqm"],"backgroundTag":"unsupported-treat-as","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}