hibernate/hibernate-orm · error · IllegalStateException
JPA selection is not compound
Error message
JPA selection is not compound
What it means
getCompoundSelectionItems() is only meaningful for compound (multi-item) selections such as multiselect/array/tuple constructions. SqmSubQuery.isCompoundSelection() returns false — a subquery is always a single scalar expression — so calling getCompoundSelectionItems() on a subquery throws IllegalStateException. Code that processes arbitrary Selection trees must branch on isCompoundSelection() first.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/query/sqm/tree/spi/select/SqmSubQuery.java:336
return (SqmExpression<T>) super.getSelection();
}
@Override
public boolean isCompoundSelection() {
// A subquery is always a single/scalar expression, so it can't be a compound selection
return false;
}
@Override
public List<? extends JpaSelection<?>> getSelectionItems() {
return Collections.emptyList();
}
@Nonnull
@Override
public List<Selection<?>> getCompoundSelectionItems() {
// A subquery is always a single/scalar expression, so it can't be a compound selection
throw new IllegalStateException( "JPA selection is not compound" );
}
@Nonnull
@Override
public SqmSubQuery<T> distinct(boolean distinct) {
super.distinct( distinct );
return this;
}
@Nonnull
@Override
public SqmSubQuery<T> where(@Nonnull Expression<Boolean> restriction) {
super.where( restriction );
return this;
}
@Nonnull
@OverrideView on GitHub (pinned to fad1729dce)
Solutions
- Check selection.isCompoundSelection() before calling getCompoundSelectionItems() and treat false as 'single expression'
- For subqueries, use getSelectionItems() (which returns an empty list for SqmSubQuery) or simply handle the subquery as one atomic item
- Reorder the mapper to switch on the concrete JpaSelection subtype instead of assuming compound
Example fix
// before
for (Selection<?> s : query.getSelectionList()) {
((CompoundSelection<?>) s).getCompoundSelectionItems(); // IllegalStateException when s is a subquery
}
// after
for (Selection<?> s : query.getSelectionList()) {
if (s.isCompoundSelection()) {
((CompoundSelection<?>) s).getCompoundSelectionItems();
} else {
handleSingleExpression(s); // subquery lands here
}
} Defensive patterns
Strategy: type-guard
Validate before calling
if (selection.isCompoundSelection()) { items = selection.getCompoundSelectionItems(); } else { /* single expression, possibly a subquery */ } Type guard
static boolean isCompound(Selection<?> s) { return s.isCompoundSelection(); } // false for SqmSubQuery Try / catch
try { items = sel.getCompoundSelectionItems(); } catch (IllegalStateException e) { if (e.getMessage().contains("not compound")) items = List.of(sel); else throw e; } Prevention
- Never cast Selection to CompoundSelection without checking isCompoundSelection() first
- Treat subqueries as atomic single expressions in result mappers
- Add a subquery to the selection in tests of generic mapping utilities
When it happens
Trigger: Generic result-mapping utilities, tuple transformers, or criteria walkers that unconditionally call getCompoundSelectionItems() (or cast Selection to CompoundSelection) on every node, encountering a Selection that is actually a Subquery.
Common situations: In-house criteria-to-SQL/DTO mappers; libraries that emulate Spring Data projections over JPA criteria; code migrated from a query type that was compound to one where a subquery now appears in the selection list.
Related errors
- Not correlated
- Correlated derived root does not have an entity type. Use ge
- Correlated derived root does not have an entity type. Use ge
- Can't set alias on a correlated root
- 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/93254f05dbe5c248.
Report an issue: GitHub.