hibernate/hibernate-orm · error · IllegalArgumentException

Illegal attempt to bind a collection value to a single-value

Error message

Illegal attempt to bind a collection value to a single-valued parameter

What it means

QueryParameterBindingImpl.assertMultivalued checks queryParameter.allowsMultiValuedBinding() before a collection is expanded into SQL parameter slots. Hibernate marks a parameter single-valued when it is not used in a position that accepts a collection (IN-style predicate), so binding a collection to it throws IllegalArgumentException. The guard fires at binding time, before SQL generation.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingImpl.java:266

		values.forEach( this::validate );
		clarifyType( values, clarifiedType );
		bindMultipleValues( values );
	}

	private void bindMultipleValues(Collection<?> coerced) {
		final List<T> list = new ArrayList<>();
		for ( var value : coerced ) {
			list.add( cast( value ) );
		}
		bindValues = list;
		bindValue = null;
		isMultiValued = true;
		isBound = true;
	}

	private void assertMultivalued() {
		if ( !queryParameter.allowsMultiValuedBinding() ) {
			throw new IllegalArgumentException(
					"Illegal attempt to bind a collection value to a single-valued parameter"
			);
		}
	}

	private void setExplicitTemporalPrecision(@SuppressWarnings("deprecation") TemporalType precision) {
		explicitTemporalPrecision = precision;
		if ( bindType == null || isTemporal( determineJavaType( bindType ) ) ) {
			bindType = resolveTemporalPrecision( precision, bindType, getCriteriaBuilder() );
		}
	}

	private JavaType<T> determineJavaType(BindableType<T> bindType) {
		return getCriteriaBuilder().resolveExpressible( bindType ).getExpressibleJavaType();
	}

	@Override
	public @Nullable MappingModelExpressible<T> getType() {

View on GitHub (pinned to fad1729dce)

Solutions

  1. Rewrite the predicate so the parameter is collection-valued: 'where x.id in :ids' (Hibernate 6 needs no parentheses).
  2. Or bind a single element with plain setParameter if the query really is single-valued.
  3. If both shapes are needed, build the query dynamically and choose '=' or 'in' together with the matching bind call.

Example fix

// before
var q = session.createQuery(
    "from Person p where p.team.id = :teamIds", Person.class);
q.setParameterList("teamIds", teamIds); // throws: '=' position is single-valued

// after
var q = session.createQuery(
    "from Person p where p.team.id in :teamIds", Person.class);
q.setParameterList("teamIds", teamIds);
Defensive patterns

Strategy: validation

Validate before calling

static void bind(org.hibernate.query.Query<?> q, String name, Object value) {
    var p = q.getParameterMetadata().getQueryParameter(name);
    if (value instanceof java.util.Collection<?> c && !p.allowsMultiValuedBinding())
        throw new IllegalArgumentException("Parameter :" + name
            + " is single-valued; bind one element or rewrite the predicate with 'in'");
    q.setParameter(name, value);
}

Prevention

When it happens

Trigger: setParameterList("ids", list) or setParameter("ids", collection) when :ids appears in a single-valued position, e.g. 'where x.id = :ids', a select expression, or an arithmetic/concat operand. Criteria parameters not used in an IN predicate are likewise single-valued.

Common situations: Changing 'x.id = :id' to accept a list of ids and forgetting to change '=' to 'in'; generic filter frameworks that always bind collections regardless of the predicate; refactoring a single value into a list without touching the HQL.

Related errors


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