hibernate/hibernate-orm · error · IllegalArgumentException
Subquery parent of all operands must match
Error message
Subquery parent of all operands must match
What it means
When applying a set operation to subqueries, Hibernate requires all operands to belong to the same enclosing query: query.getParent() must be reference-equal for every operand. A subquery is scoped to the AbstractQuery it was created from, and mixing subqueries from different parents (or from a different level of the tree) produces an SqmQueryGroup whose parts Hibernate cannot legally attach, so it fails fast with IllegalArgumentException.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java:798
);
}
@SuppressWarnings("unchecked")
private <T> JpaSubQuery<T> setOperation(
SetOperator operator,
Subquery<? extends T> subquery,
Subquery<?>... queries) {
final var resultType = (Class<T>) subquery.getResultType();
final var parent = (SqmQuery<T>) subquery.getParent();
final List<SqmQueryPart<T>> queryParts = new ArrayList<>( queries.length + 1 );
final Map<String, SqmCteStatement<?>> cteStatements = new LinkedHashMap<>();
collectQueryPartsAndCtes( (SqmSelectQuery<T>) subquery, queryParts, cteStatements );
for ( var query : queries ) {
if ( query.getResultType() != resultType ) {
throw new IllegalArgumentException( "Result type of all operands must match" );
}
if ( query.getParent() != parent ) {
throw new IllegalArgumentException( "Subquery parent of all operands must match" );
}
collectQueryPartsAndCtes( (SqmSelectQuery<T>) query, queryParts, cteStatements );
}
return new SqmSubQuery<>(
parent,
new SqmQueryGroup<>( this, operator, queryParts ),
resultType,
cteStatements,
this
);
}
private <T> void collectQueryPartsAndCtes(
SqmSelectQuery<T> query,
List<SqmQueryPart<T>> queryParts,
Map<String, SqmCteStatement<?>> cteStatements) {
queryParts.add( query.getQueryPart() );
for ( var cteStatement : query.getCteStatements() ) {View on GitHub (pinned to fad1729dce)
Solutions
- Create all operand subqueries from the same enclosing query object: call query.subquery(...) once per operand on the identical AbstractQuery instance.
- Pass the owning query into your subquery-building helpers so parents always match.
- If operands conceptually belong to different queries, restructure: perform the set operation at the CriteriaQuery level instead of the Subquery level.
Example fix
// before Subquery<Long> idsA = queryA.subquery(Long.class); // parent = queryA Subquery<Long> idsB = queryB.subquery(Long.class); // parent = queryB cb.union(idsA, idsB); // throws: Subquery parent of all operands must match // after Subquery<Long> idsA = query.subquery(Long.class); // both from the same 'query' Subquery<Long> idsB = query.subquery(Long.class); cb.union(idsA, idsB);
Defensive patterns
Strategy: validation
Validate before calling
boolean sameParent(Subquery<?> first, Subquery<?>... rest) {
AbstractQuery<?> parent = first.getParent();
for (Subquery<?> s : rest) if (s.getParent() != parent) return false;
return true;
}
if (!sameParent(s1, s2)) throw new IllegalStateException("subquery operands belong to different queries"); Type guard
static boolean sharesParent(Subquery<?> a, Subquery<?> b) {
return a.getParent() == b.getParent();
} Try / catch
try {
cb.union(s1, s2);
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("Subquery parent")) { /* recreate both subqueries from the same owner */ }
else throw e;
} Prevention
- Create operand subqueries inside the same method that owns the outer query so parents cannot diverge.
- Pass the owning AbstractQuery into subquery-building helpers as a parameter.
- Never cache Subquery instances for reuse across queries.
When it happens
Trigger: cb.union(subA, subB) where subA = outerQuery.subquery(X.class) and subB = otherQuery.subquery(X.class) (two different CriteriaQuery instances); a subquery created from a subquery (nested scope) unioned with one created from the top-level query; reusing a cached/helper subquery in a new query.
Common situations: Utility methods that build reusable subqueries parameterized by an outer query, then called with two different outers; refactoring a query builder so helper subqueries are created against the wrong owner; copy-pasting subquery construction across query builders.
Related errors
- Result type of all operands must match
- Different CTE with same name [%s] found in different set ope
- Domain result for non-scalar subquery shouldn't be created
- Can't render offset and fetch clause for subquery
- Can't render offset and fetch clause for subquery
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/949d7e331f7b64a4.
Report an issue: GitHub.