hibernate/hibernate-orm · error · UnsupportedOperationException

Can't infer collection type based on element expression: {}

Error message

Can't infer collection type based on element expression: {}

What it means

When a criteria value/parameter binding wraps a java.util.Collection, Hibernate tries to determine the collection's element BasicType from an 'element type inference source' expression. collectionValueParameter resolves that expression's SqmType; if it is null (the expression is untyped — e.g. a raw parameter or an expression whose type was never resolvable), it throws UnsupportedOperationException because it cannot pick a list SqlType for the bind.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java:2632

			final Object coercedValue = javaType.coerce( value );
			// ignore typeInferenceSource and fall back to the value type
			if ( isInstance( bindableType, coercedValue ) ) {
				@SuppressWarnings("unchecked") // safe, we just checked
				final var widerType = (BindableType<? super T>) bindableType;
				return new ValueBindJpaCriteriaParameter<>( widerType, javaType.cast( coercedValue ), this );
			}
			else {
				return new ValueBindJpaCriteriaParameter<>( getParameterBindType( value ), value, this );
			}
		}
	}

	private <E> ValueBindJpaCriteriaParameter<? extends Collection<E>> collectionValueParameter(Collection<E> value, SqmExpression<E> elementTypeInferenceSource) {
		final var elementType =
				resolveExpressible( bindableType( elementTypeInferenceSource ) )
						.getSqmType();
		if ( elementType == null ) {
			throw new UnsupportedOperationException( "Can't infer collection type based on element expression: " + elementTypeInferenceSource );
		}
		final var collectionType = DdlTypeHelper.resolveListType( elementType, getTypeConfiguration() );
		//noinspection unchecked
		return new ValueBindJpaCriteriaParameter<>( (BasicType<Collection<E>>) collectionType, value, this );
	}

	private static <E> BindableType<E> bindableType(SqmExpression<E> elementTypeInferenceSource) {
		if ( elementTypeInferenceSource != null ) {
			if ( elementTypeInferenceSource instanceof BindableType ) {
				//noinspection unchecked
				return (BindableType<E>) elementTypeInferenceSource;
			}
			else if ( elementTypeInferenceSource.getNodeType() != null ) {
				return elementTypeInferenceSource.getNodeType();
			}
		}
		return null;
	}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Give the inference source a concrete type: bind against a typed path (root.get("status")) or wrap the value with an explicitly typed parameter (cb.parameter(List.class) plus setParameter with typed list).
  2. Prefer the standard IN pattern: path.in(collection) or cb.in(path).value(...), which types itself from the path.
  3. If the element type is known up front, construct the ValueBindJpaCriteriaParameter with an explicitly typed expression (e.g. cb.literal(firstElement) or cb.treat(...)/cb.as(...) cast).

Example fix

// before
Expression<Collection<String>> in = cb.value(statusCodes); // element expression untyped -> UnsupportedOperationException

// after
Predicate p = root.get("status").in(statusCodes); // typed from the path
// or with an explicit parameter:
ParameterExpression<List<String>> p1 = cb.parameter(List.class);
query.where(root.get("status").in(p1));
q.setParameter(p1, statusCodes);
Defensive patterns

Strategy: validation

Validate before calling

// Bind collections through typed paths/parameters instead of untyped value binds
boolean pathTyped(Expression<?> e) {
    return e instanceof Path<?> p && p.getModel() != null;
}
if (!pathTyped(elementSource)) throw new IllegalArgumentException("need a typed path to infer collection element type");

Type guard

static boolean hasResolvableType(Expression<?> e) {
    return e instanceof Path<?> p && p.getModel() != null; // paths carry attribute types
}

Try / catch

try {
    expr = cb.value(statusCodes); // collection bind
} catch (UnsupportedOperationException e) {
    if (e.getMessage().contains("infer collection type")) { /* use root.get("status").in(statusCodes) instead */ }
    else throw e;
}

Prevention

When it happens

Trigger: Passing a Collection value through cb.value(collection) / ValueBindJpaCriteriaParameter creation where the inference-source expression is itself an untyped JpaCriteriaParameter or a generic SqmExpression with null SqmType; building IN-list bindings dynamically against an expression that carries no type information.

Common situations: Generic filter frameworks that bind collections via value(...) against opaque Expression<?> placeholders; reusing an expression taken from a different builder/query before its type was established; Hibernate version changes that tightened when expression types get resolved.

Related errors


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