hibernate/hibernate-orm · error · IllegalArgumentException
Criteria did not define any query roots
Error message
Criteria did not define any query roots
What it means
Thrown as IllegalArgumentException by SqmUtil.validateCriteriaQueryStructure when a CriteriaQuery part has an empty select clause and its from-clause has no roots. When nothing is selected, Hibernate implicitly selects the single root — with zero roots there is nothing to query, so criteria validation fails before execution. The check recurses into query groups, so a set-operation member without roots also triggers it.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmUtil.java:1032
Restriction<? super X> restriction) {
//noinspection unchecked
final var root = (JpaRoot<X>) sqmStatement.getRoot( 0, resultType );
return (SqmPredicate) restriction.toPredicate( root, sqmStatement.nodeBuilder() );
}
public static void validateCriteriaQuery(SqmQueryPart<?> queryPart) {
validateCriteriaQueryStructure( queryPart );
SqmCriteriaRootValidator.validate( queryPart );
}
private static void validateCriteriaQueryStructure(SqmQueryPart<?> queryPart) {
if ( queryPart instanceof SqmQuerySpec<?> sqmQuerySpec ) {
final var selectClause = sqmQuerySpec.getSelectClause();
if ( selectClause.getSelections().isEmpty() ) {
// make sure there is at least one root
final var sqmRoots = sqmQuerySpec.getFromClause().getRoots();
if ( sqmRoots == null || sqmRoots.isEmpty() ) {
throw new IllegalArgumentException( "Criteria did not define any query roots" );
}
if ( sqmRoots.size() != 1 ) {
throw new IllegalArgumentException( "Criteria has multiple query roots" );
}
}
}
else if ( queryPart instanceof SqmQueryGroup<?> queryGroup ) {
for ( var part : queryGroup.getQueryParts() ) {
validateCriteriaQueryStructure( part );
}
}
else {
assert false;
}
}
public static void validateCriteriaTree(SqmDeleteStatement<?> statement) {
SqmCriteriaRootValidator.validate( statement );View on GitHub (pinned to fad1729dce)
Solutions
- Add at least one root: cq.from(Person.class)
- If the criteria is optional, skip query execution entirely when no root/entity was configured
- Guard dynamic criteria builders: require the entity class parameter up front and call from(entityClass) unconditionally
- For query groups, ensure every member part has its own root
Example fix
// before CriteriaQuery<Person> cq = cb.createQuery(Person.class); List<Person> people = em.createQuery(cq).getResultList(); // no from() called // after CriteriaQuery<Person> cq = cb.createQuery(Person.class); cq.from(Person.class); List<Person> people = em.createQuery(cq).getResultList();
Defensive patterns
Strategy: validation
Validate before calling
static <X> CriteriaQuery<X> requireRoot(CriteriaQuery<X> cq, Class<X> entity) {
if (cq.getRoots().isEmpty()) {
cq.from(entity);
}
return cq;
}
// em.createQuery(requireRoot(cq, Person.class)).getResultList(); Try / catch
try {
return em.createQuery(cq).getResultList();
} catch (IllegalArgumentException e) {
if (e.getMessage() != null && e.getMessage().contains("query roots")) {
throw new IllegalStateException("Criteria built without a root: " + cq, e);
}
throw e;
} Prevention
- Always call from(entityClass) immediately after cb.createQuery()
- Guard dynamic criteria builders with a non-null entity parameter
- Skip execution and return an empty list when no entity type is configured
When it happens
Trigger: em.createQuery(cb.createQuery()) or cb.createQuery(Person.class) used without ever calling .from(Person.class); a dynamically assembled CriteriaQuery whose from() call was skipped by a filter/condition bug; a CriteriaDefinition or query group where one branch never got a root.
Common situations: Criteria built from user-supplied filter specs where an empty filter set leads to an empty query; refactoring that moves from() into a conditional block; copy-paste criteria templates missing the from() line; new tests constructing criteria stubs.
Related errors
- Criteria has multiple query roots
- Could not resolve entity class '{}'
- MappedSuperclassType cannot be used to create an SqmPath - t
- CTE joins can not be treated
- Lateral joins can only be left or inner. Illegal join type:
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/120c58faf6672de0.
Report an issue: GitHub.