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
- Create every path from the query's root (root.join(...), root.get(...)) instead of constructing SqmPath nodes directly
- When cloning SQM trees, use node.copy(SqmCopyContext) so LHS links are preserved
- Correlate subqueries through createCorrelation()/SqmSubQuery so the LHS chain stays intact
- 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
- Always create paths from the query root (root.join/root.get), never standalone SqmPath objects
- Use node.copy(SqmCopyContext) when cloning SQM subtrees so LHS links survive
- Never share Root/Join/SQM nodes between different queries
- Correlate subqueries via createCorrelation() instead of manual wiring
- If hit with plain HQL and no manual SQM, minimize and report - it indicates a parser defect
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
- Could not resolve attribute '%s' of '%s' due to the attribut
- Could not interpret attribute '%s' of basic-valued path '%s'
- Boolean expression does not support max()
- Boolean expression does not support min()
- Cte root does not have an entity type. Use getReferencedPath
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/b4eb00fe5022d81d.
Report an issue: GitHub.