hibernate/hibernate-orm · error · ParsingException

Could not find root

Error message

Could not find root

What it means

SqmPath.findRoot() walks the getLhs() chain to find the SqmRoot a path belongs to; roots and root-like joins (SqmRoot, SqmCrossJoin, SqmEntityJoin, SqmCteJoin, SqmDerivedJoin, correlations) override it to return themselves. If the walk reaches a path whose LHS is null without meeting a root - a detached or partially built SQM path - the default method throws ParsingException('Could not find root') from org.hibernate.query.sqm. It fires during SQM construction (join registration in AbstractSqmFrom, join-predicate resolution in QualifiedJoinPredicatePathConsumer), so it surfaces as a parse failure of the HQL/criteria query being built.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/query/sqm/tree/spi/domain/SqmPath.java:131

	<S extends T> SqmTreatedPath<T,S> treatAs(@Nonnull Class<S> treatJavaType, @Nullable String alias);

	@Nonnull
	<S extends T> SqmTreatedPath<T,S> treatAs(@Nonnull EntityDomainType<S> treatTarget, @Nullable String alias);

	@Nonnull
	<S extends T> SqmTreatedPath<T,S> treatAs(@Nonnull Class<S> treatJavaType, @Nullable String alias, boolean fetch);

	@Nonnull
	<S extends T> SqmTreatedPath<T,S> treatAs(@Nonnull EntityDomainType<S> treatTarget, @Nullable String alias, boolean fetch);

	@Nonnull
	default SqmRoot<?> findRoot() {
		final var lhs = getLhs();
		if ( lhs != null ) {
			return lhs.findRoot();
		}

		throw new ParsingException( "Could not find root" );
	}

	SqmPath<?> resolvePathPart(
			String name,
			boolean isTerminal,
			SqmCreationState creationState);

	@Override
	default SqmPath<?> resolveIndexedAccess(
			SqmExpression<?> selector,
			boolean isTerminal,
			SqmCreationState creationState) {
		throw new SemanticException( "Index operator applied to non-plural path '" + getNavigablePath() + "'" );
	}

	/**
	 * Get this path's actual resolved model, i.e. the concrete type for generic attributes.
	 */

View on GitHub (pinned to fad1729dce)

Solutions

  1. Create every path from the query's root (root.join(...), root.get(...)) instead of constructing SqmPath nodes directly
  2. When cloning SQM trees, use node.copy(SqmCopyContext) so LHS links are preserved
  3. Correlate subqueries through createCorrelation()/SqmSubQuery so the LHS chain stays intact
  4. If no manual SQM is involved, reduce the HQL to a minimal reproduction and report it as a Hibernate parser bug

Example fix

// before - detached path, lhs is null
SqmPath<Phone> p = phonePathSource.createSqmPath( null );
p.findRoot(); // ParsingException: Could not find root

// after - build from the query's root
SqmRoot<Person> root = query.from( Person.class );
SqmMapJoin<Person, String, Phone> phones = root.join( Person_.phones );
phones.findRoot(); // returns root
Defensive patterns

Strategy: try-catch

Validate before calling

import org.hibernate.query.sqm.tree.spi.domain.SqmPath;
import org.hibernate.query.sqm.tree.spi.from.SqmRoot;

/** Returns the root this path belongs to, or null if the path is detached. */
static SqmRoot<?> rootOrNull(SqmPath<?> path) {
    for ( SqmPath<?> p = path; p != null; p = p.getLhs() ) {
        if ( p instanceof SqmRoot<?> r ) {
            return r;
        }
    }
    return null; // findRoot() on such a path throws 'Could not find root'
}

Type guard

static boolean isAttachedToRoot(SqmPath<?> path) {
    for ( SqmPath<?> p = path; p != null; p = p.getLhs() ) {
        if ( p instanceof SqmRoot<?> ) {
            return true;
        }
    }
    return false;
}

Try / catch

try {
    return session.createQuery( hql, Person.class ).list();
} catch ( org.hibernate.query.sqm.ParsingException e ) {
    if ( "Could not find root".equals( e.getMessage() ) ) {
        throw new IllegalStateException( "Query uses a path detached from its root: " + hql, e );
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling findRoot() - directly or indirectly via join creation (SqmFrom.join/addOrderedJoin) or HQL join-predicate parsing - on an SqmPath that is not linked to an SqmRoot: hand-built SQM trees, a path created standalone from its SqmPathSource (lhs never set), or SQM nodes copied/reused between queries without SqmCopyContext.

Common situations: Programmatic SQM construction in custom query infrastructure or tests; reusing Root/Join objects from one criteria query inside another; partial copies of SQM subtrees; Hibernate version upgrades that changed LHS wiring - if hit with plain HQL and no manual SQM, treat it as a parser regression.

Related errors


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