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

  1. Create all operand subqueries from the same enclosing query object: call query.subquery(...) once per operand on the identical AbstractQuery instance.
  2. Pass the owning query into your subquery-building helpers so parents always match.
  3. 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

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


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