hibernate/hibernate-orm · error · SemanticException
Fetch join has a 'with' clause (use a filter instead)
Error message
Fetch join has a 'with' clause (use a filter instead)
What it means
consumeJoin rejects a fetch join that declares a 'with' restriction ('join fetch e.tasks t with t.done = true'). A conditional fetch join would build partially initialized collections and break fetch semantics, so Hibernate throws SemanticException and points to filters as the supported way to restrict fetched associations.
Solutions
- Use a plain (non-fetch) join with the condition if you only need filtered tuples: 'join e.tasks t where t.active = true'
- Apply @FilterDef/@Filter on the association so fetched rows are restricted consistently, then fetch without 'with'
- Model the restricted set as a separate association or query it separately to keep fetch semantics intact
Example fix
// before select e from Employee e join fetch e.tasks t with t.active = true // after (filtered tuples, no eager collection init) select e from Employee e join e.tasks t where t.active = true // or restrict the association via @Filter and fetch plainly select e from Employee e join fetch e.tasks
Defensive patterns
Strategy: validation
Validate before calling
// Reject 'join fetch ... with ...' before execution
static boolean conditionalFetchJoin(String hql) {
return java.util.regex.Pattern.compile("join\\s+fetch\\s+[^;]+?\\s+with\\s", java.util.regex.Pattern.CASE_INSENSITIVE | java.util.regex.Pattern.DOTALL).matcher(hql).find();
}
if (conditionalFetchJoin(hql)) throw new IllegalArgumentException("Fetch joins cannot have 'with' conditions - use filters or a plain join"); Try / catch
try {
return em.createQuery(hql, Employee.class).getResultList();
} catch (org.hibernate.query.sqm.SemanticException e) {
if (e.getMessage() != null && e.getMessage().startsWith("Fetch join has a 'with' clause")) {
throw new IllegalArgumentException("Split into a plain join for filtering, or use @Filter on the association", e);
}
throw e;
} Prevention
- Never put row conditions on fetch joins; conditionally fetched collections end up partially initialized
- Model association-level restrictions with @FilterDef/@Filter so they apply wherever the collection is fetched
- If filtered tuples (not filtered collections) are the goal, use a plain join plus where
When it happens
Trigger: 'select e from Employee e join fetch e.tasks t with t.active = true'; any 'join fetch ... with <predicate>' in HQL.
Common situations: Trying to load only part of a collection eagerly (the classic XY problem); porting native SQL left joins with conditions into fetch joins; upgrading Hibernate where older versions tolerated some forms.
Related errors
- ALIASED_FETCH_JOIN
- The 'from' clause of a subquery has a 'fetch'
- The 'from' clause of a subquery has a 'fetch' join
- A CTE with the label
- Access to from node '" + from.getCorrelationParent() + "'…
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/dcec4035b35f0dc2.
Report an issue: GitHub.
Appendix: source
Thrown at hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java:2309
// Joins are allowed to be reused if they don't have a join condition
final var allowReuse = joinRestrictionContext == null;
dotIdentifierConsumerStack.push( new QualifiedJoinPathConsumer( sqmRoot, joinType, fetch, alias, allowReuse, this ) );
try {
final var join = getJoin( sqmRoot, joinType, qualifiedJoinTargetContext, alias, fetch );
if ( join instanceof SqmEntityJoin<?,?> || join instanceof SqmDerivedJoin<?> || join instanceof SqmCteJoin<?> ) {
sqmRoot.addSqmJoin( join );
}
else if ( join instanceof SqmAttributeJoin<?, ?> attributeJoin ) {
if ( getCreationOptions().useStrictJpaCompliance() ) {
if ( join.getExplicitAlias() != null && attributeJoin.isFetched() ) {
throw new StrictJpaComplianceViolation(
"Encountered aliased fetch join, but strict JPQL compliance was requested",
StrictJpaComplianceViolation.Type.ALIASED_FETCH_JOIN
);
}
}
if ( joinRestrictionContext != null && attributeJoin.isFetched() ) {
throw new SemanticException( "Fetch join has a 'with' clause (use a filter instead)", query );
}
}
if ( joinRestrictionContext != null ) {
dotIdentifierConsumerStack.push( new QualifiedJoinPredicatePathConsumer( join, this ) );
try {
join.setJoinPredicate( (SqmPredicate) joinRestrictionContext.getChild( 1 ).accept( this ) );
}
finally {
dotIdentifierConsumerStack.pop();
}
}
}
finally {
dotIdentifierConsumerStack.pop();
}
}
View on GitHub (pinned to fad1729dce)