hibernate/hibernate-orm · error · IllegalArgumentException

LHS cannot be null for a sub-navigable reference - {}

Error message

LHS cannot be null for a sub-navigable reference - {}

What it means

SingularAttributeImpl.createNavigablePath(SqmPath parent, alias) requires a non-null parent (the LHS — root or join the attribute hangs off). A null parent means a sub-navigable reference was requested with nothing to attach to, so it throws IllegalArgumentException naming the attribute. Reached through SQM building: criteria paths, HQL translation, or direct SQM metamodel use.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/SingularAttributeImpl.java:383

			);
			return (SqmJoin<D, J>) join;
		}
		else {
			return new SqmSingularJoin<>(
					lhs,
					this,
					alias,
					joinType,
					fetched,
					nodeBuilder
			);
		}
	}

	@Override
	public NavigablePath createNavigablePath(SqmPath<?> parent, @Nullable String alias) {
		if ( parent == null ) {
			throw new IllegalArgumentException(
					"LHS cannot be null for a sub-navigable reference - " + getName()
			);
		}

		return buildSubNavigablePath( getParentNavigablePath( parent ), getName(), alias );
	}

	public static class ComparableAttributeImpl<D, J extends Comparable<? super J>>
			extends SingularAttributeImpl<D, J>
			implements ComparableAttribute<D, J> {
		public ComparableAttributeImpl(
				ManagedDomainType<D> declaringType,
				String name,
				AttributeClassification attributeClassification,
				SqmDomainType<J> attributeType,
				JavaType<?> relationalJavaType,
				Member member,
				boolean isIdentifier,

View on GitHub (pinned to fad1729dce)

Solutions

  1. Create the root/join first and derive attribute paths from it: root.get(attribute) / from.join(...)
  2. In custom SQM code, pass the enclosing SqmFrom as lhs — never null
  3. Check for null parent before calling createNavigablePath and fail with your own diagnostic naming the attribute and query

Example fix

// before (custom SQM building)
SqmPath<?> p = singularAttribute.createSqmPath(null, null); // 'LHS cannot be null'

// after
SqmRoot<Order> root = query.getRoots().iterator().next();
SqmPath<?> p = singularAttribute.createSqmPath(root, null); // attached to a lhs
Defensive patterns

Strategy: validation

Validate before calling

// Custom SQM building: always establish the lhs first and pass it through
if (parent == null) {
  throw new IllegalArgumentException(
      "Cannot create path for attribute '" + attribute.getName() + "' without a parent; create the root/join first");
}
SqmPath<?> path = attribute.createSqmPath(parent, alias);

Try / catch

try {
  return attribute.createSqmPath(lhs, alias);
} catch (IllegalArgumentException e) {
  if (e.getMessage() != null && e.getMessage().startsWith("LHS cannot be null")) {
    throw new IllegalStateException("Path for '" + attribute.getName() + "' built without a root — check path construction order", e);
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling attribute.createSqmPath(null) or building an SqmPath for an attribute without first creating the SqmFrom it belongs to (root.get(...) done out of order); custom criteria extensions or QueryEngine integrations constructing paths top-down without a lhs.

Common situations: Custom query infrastructure (tenant filters, specification libraries) that builds paths from metamodel attributes; refactors that reorder path construction; code ported from native Criteria where path chaining differs.

Related errors


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