hibernate/hibernate-orm · error · IllegalArgumentException

Not enough root entities

Error message

Not enough root entities

What it means

AbstractSqmSelectQuery.getRoot(int position, Class type) throws IllegalArgumentException when the requested position is >= the number of roots in the query spec — i.e. the query has fewer FROM entities than the index you asked for. Hibernate's criteria API supports multiple roots (cross join style `from A a, B b`), and getRoot(position, ...) is positional access into that root list; calling it with position 1 when only one root exists, or position 2 when two exist, fails here before the castRoot type check even runs.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/query/sqm/tree/spi/select/AbstractSqmSelectQuery.java:274

		return new HashSet<>( getQuerySpec().getRoots() );
	}

	/**
	 * @see org.hibernate.query.criteria.JpaCriteriaQuery#getRootList()
	 */
	@Nonnull
	public List<Root<?>> getRootList() {
		return new ArrayList<>( getQuerySpec().getRootList() );
	}

	/**
	 * @see org.hibernate.query.criteria.JpaCriteriaQuery#getRoot(int, Class)
	 */
	@Nonnull
	public <E> JpaRoot<? extends E> getRoot(int position, Class<E> type) {
		final var rootList = getQuerySpec().getRootList();
		if ( rootList.size() <= position ) {
			throw new IllegalArgumentException( "Not enough root entities" );
		}
		return castRoot( rootList.get( position ), type );
	}

	/**
	 * @see org.hibernate.query.criteria.JpaCriteriaQuery#getRoot(String, Class)
	 */
	@Nonnull
	public <E> JpaRoot<? extends E> getRoot(String alias, Class<E> type) {
		for ( var root : getQuerySpec().getRootList() ) {
			final String rootAlias = root.getAlias();
			if ( rootAlias != null && rootAlias.equals( alias ) ) {
				return castRoot( root, type );
			}
		}
		throw new IllegalArgumentException( "No root entity with alias " + alias );
	}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Use `query.getRootList().size()` (or getRoots()) to bound the position before calling getRoot(position, type)
  2. Prefer alias-based lookup `query.getRoot("b", B.class)` or keep references to the Root objects returned by from()
  3. When roots are conditional, store the from() results in variables/map instead of relying on positional indexes
  4. Add roots first, then resolve positions, in that order

Example fix

// before
JpaCriteriaQuery<Tuple> q = cb.createTupleQuery();
q.from( Person.class );
JpaRoot<? extends Address> a = q.getRoot( 1, Address.class ); // IllegalArgumentException

// after
q.from( Address.class );
JpaRoot<? extends Address> a = q.getRoot( 1, Address.class );
Defensive patterns

Strategy: validation

Validate before calling

int roots = query.getRootList().size();
if (position >= roots) {
    throw new IllegalArgumentException("Only " + roots + " root(s); position " + position + " invalid");
}
JpaRoot<? extends E> r = query.getRoot(position, type);

Prevention

When it happens

Trigger: Code attempts to resolve a single root from a select query that has zero roots registered in its query spec.

Common situations: Forgetting to call CriteriaQuery.from() before building selections or predicates that need a root.

Related errors


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