hibernate/hibernate-orm · error · SemanticException

The 'from' clause of a subquery has a 'fetch'

Error message

The 'from' clause of a subquery has a 'fetch'

What it means

consumeJoin throws when the 'fetch' keyword appears on a join inside a subquery (processingStateStack.depth() > 1). Fetching is only defined for the outermost query: a subquery produces intermediate results, not managed results whose associations could be initialized, so 'fetch' in a nested from clause is a semantic error.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java:2287

	}

	private JpaMetamodel getJpaMetamodel() {
		return getCreationContext().getJpaMetamodel();
	}

	@Override
	public final SqmJoin<?, ?> visitJoin(HqlParser.JoinContext parserJoin) {
		throw new UnsupportedOperationException( "Unexpected call to #visitJoin, see #consumeJoin" );
	}

	protected <X> void consumeJoin(HqlParser.JoinContext parserJoin, SqmRoot<X> sqmRoot) {
		final var joinType = getSqmJoinType( parserJoin.joinType() );
		final var qualifiedJoinTargetContext = parserJoin.joinTarget();
		final String alias = extractAlias( getVariable( qualifiedJoinTargetContext ) );
		final boolean fetch = parserJoin.FETCH() != null;

		if ( fetch && processingStateStack.depth() > 1 ) {
			throw new SemanticException( "The 'from' clause of a subquery has a 'fetch'", query );
		}

		final var joinRestrictionContext = parserJoin.joinRestriction();
		// 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
						);

View on GitHub (pinned to fad1729dce)

Solutions

  1. Remove 'fetch' from the join inside the subquery
  2. Move the fetch join to the outer query: 'select e from Employee e left join fetch e.items'
  3. Use a plain join in the subquery - fetching inside it has no effect anyway

Example fix

// before
select e from Employee e
where exists (select 1 from e.items i join fetch i.subTasks st)

// after
select e from Employee e left join fetch e.items i
where exists (select 1 from Employee e2 where e2.id = e.id and size(e2.items) > 0)
Defensive patterns

Strategy: validation

Validate before calling

// Lint generated HQL: 'fetch' must not appear inside a subquery's from clause
static boolean fetchInsideSubquery(String hql) {
    java.util.regex.Matcher m = java.util.regex.Pattern.compile("\\((?:select\\s+).*?\\)", java.util.regex.Pattern.CASE_INSENSITIVE | java.util.regex.Pattern.DOTALL).matcher(hql);
    while (m.find()) {
        if (m.group().toLowerCase().contains(" join fetch ")) return true;
    }
    return false;
}

Try / catch

try {
    return em.createQuery(hql, Employee.class).getResultList();
} catch (org.hibernate.query.sqm.SemanticException e) {
    if (e.getMessage() != null && e.getMessage().contains("subquery has a 'fetch'")) {
        throw new IllegalArgumentException("Remove 'fetch' from joins inside subqueries; fetch in the outer query instead", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: 'select e from Employee e where exists (select 1 from e.items i join fetch i.subTasks st)'; any join marked 'fetch' inside a subquery from clause.

Common situations: Copying an outer fetch join into a subquery while refactoring an exists/in predicate; ORMs/query DSLs that append 'fetch' to every join uniformly.

Related errors


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