{"record":{"id":"8b687f486f31e08c","repo":"hibernate/hibernate-orm","slug":"can-t-infer-collection-type-based-on-element-expre","errorCode":null,"errorMessage":"Can't infer collection type based on element expression: {}","messagePattern":"Can't infer collection type based on element expression: (.+?)","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java","lineNumber":2632,"sourceCode":"\t\t\tfinal Object coercedValue = javaType.coerce( value );\n\t\t\t// ignore typeInferenceSource and fall back to the value type\n\t\t\tif ( isInstance( bindableType, coercedValue ) ) {\n\t\t\t\t@SuppressWarnings(\"unchecked\") // safe, we just checked\n\t\t\t\tfinal var widerType = (BindableType<? super T>) bindableType;\n\t\t\t\treturn new ValueBindJpaCriteriaParameter<>( widerType, javaType.cast( coercedValue ), this );\n\t\t\t}\n\t\t\telse {\n\t\t\t\treturn new ValueBindJpaCriteriaParameter<>( getParameterBindType( value ), value, this );\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate <E> ValueBindJpaCriteriaParameter<? extends Collection<E>> collectionValueParameter(Collection<E> value, SqmExpression<E> elementTypeInferenceSource) {\n\t\tfinal var elementType =\n\t\t\t\tresolveExpressible( bindableType( elementTypeInferenceSource ) )\n\t\t\t\t\t\t.getSqmType();\n\t\tif ( elementType == null ) {\n\t\t\tthrow new UnsupportedOperationException( \"Can't infer collection type based on element expression: \" + elementTypeInferenceSource );\n\t\t}\n\t\tfinal var collectionType = DdlTypeHelper.resolveListType( elementType, getTypeConfiguration() );\n\t\t//noinspection unchecked\n\t\treturn new ValueBindJpaCriteriaParameter<>( (BasicType<Collection<E>>) collectionType, value, this );\n\t}\n\n\tprivate static <E> BindableType<E> bindableType(SqmExpression<E> elementTypeInferenceSource) {\n\t\tif ( elementTypeInferenceSource != null ) {\n\t\t\tif ( elementTypeInferenceSource instanceof BindableType ) {\n\t\t\t\t//noinspection unchecked\n\t\t\t\treturn (BindableType<E>) elementTypeInferenceSource;\n\t\t\t}\n\t\t\telse if ( elementTypeInferenceSource.getNodeType() != null ) {\n\t\t\t\treturn elementTypeInferenceSource.getNodeType();\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}","sourceCodeStart":2614,"sourceCodeEnd":2650,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java#L2614-L2650","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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).","Prefer the standard IN pattern: path.in(collection) or cb.in(path).value(...), which types itself from the path.","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)."],"exampleFix":"// before\nExpression<Collection<String>> in = cb.value(statusCodes); // element expression untyped -> UnsupportedOperationException\n\n// after\nPredicate p = root.get(\"status\").in(statusCodes); // typed from the path\n// or with an explicit parameter:\nParameterExpression<List<String>> p1 = cb.parameter(List.class);\nquery.where(root.get(\"status\").in(p1));\nq.setParameter(p1, statusCodes);","handlingStrategy":"validation","validationCode":"// Bind collections through typed paths/parameters instead of untyped value binds\nboolean pathTyped(Expression<?> e) {\n    return e instanceof Path<?> p && p.getModel() != null;\n}\nif (!pathTyped(elementSource)) throw new IllegalArgumentException(\"need a typed path to infer collection element type\");","typeGuard":"static boolean hasResolvableType(Expression<?> e) {\n    return e instanceof Path<?> p && p.getModel() != null; // paths carry attribute types\n}","tryCatchPattern":"try {\n    expr = cb.value(statusCodes); // collection bind\n} catch (UnsupportedOperationException e) {\n    if (e.getMessage().contains(\"infer collection type\")) { /* use root.get(\"status\").in(statusCodes) instead */ }\n    else throw e;\n}","preventionTips":["Prefer path.in(collection) for IN-lists — the path supplies the element type.","Always pair collection values with a typed inference source (a path or explicitly typed parameter).","Avoid generic Expression<?> placeholders in filter frameworks; keep the driving path in the predicate."],"tags":["hibernate","criteria","parameter-binding","type-inference","collection"],"backgroundTag":"unresolvable-parameter-type","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}