hibernate/hibernate-orm · error · SemanticException

Operand of 'member of' operator must be a plural path

Error message

Operand of 'member of' operator must be a plural path

What it means

isMember/isNotMember compile to an SQL MEMBER OF, which requires the collection operand to be a path to a plural attribute. createSqmMemberOfPredicate only accepts SqmPluralValuedSimplePath; passing any other expression (a literal, function result, subquery, joined entity, or singular attribute path) throws SemanticException.

Source

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

	@Nonnull
	@Override
	public <E, C extends Collection<E>> SqmPredicate isNotMember(@Nonnull Expression<E> elem, @Nonnull Expression<C> collection) {
		return createSqmMemberOfPredicate( (SqmExpression<?>) elem, (SqmPath<?>) collection, true);
	}

	@Nonnull
	@Override
	public <E, C extends Collection<E>> SqmPredicate isNotMember(E elem, @Nonnull Expression<C> collection) {
		return createSqmMemberOfPredicate( value( elem ), (SqmPath<?>) collection, true);
	}

	private SqmMemberOfPredicate createSqmMemberOfPredicate(SqmExpression<?> elem, SqmPath<?> collection, boolean negated) {
		if ( collection instanceof SqmPluralValuedSimplePath<?> pluralValuedSimplePath ) {
			return new SqmMemberOfPredicate( elem, pluralValuedSimplePath, negated, this );
		}
		else {
			throw new SemanticException( "Operand of 'member of' operator must be a plural path" );
		}
	}

	@Nonnull
	@Override
	public SqmPredicate like(@Nonnull Expression<String> searchString, @Nonnull Expression<String> pattern) {
		return new SqmLikePredicate(
				(SqmExpression<?>) searchString,
				(SqmExpression<?>) pattern,
				this
		);
	}

	@Nonnull
	@Override
	public SqmPredicate like(@Nonnull Expression<String> searchString, @Nonnull String pattern) {
		return new SqmLikePredicate(
				(SqmExpression<?>) searchString,

View on GitHub (pinned to fad1729dce)

Solutions

  1. Use the actual plural attribute path: cb.isMember(tag, root.get("tags")) or orderRoot.get("lineItems").
  2. For literal collections, use IN instead: cb.literal(value).in? no — use cb.in(...): path.in(values) or cb.isMember alternative cb.in(root.get("x")).value(...).
  3. If you joined through the entity side, switch to the collection side (order.get("items") rather than customer.join(...).get(...)).

Example fix

// before
Predicate p = cb.isMember(statusCode, root.get("customer")); // @ManyToOne -> SemanticException

// after
Predicate p = cb.isMember(statusCode, root.get("customer").get("accounts")); // @OneToMany path
// or for a plain value list use IN:
Predicate in = root.get("status").in(statusCodes);
Defensive patterns

Strategy: type-guard

Validate before calling

boolean isPluralPath(Expression<?> collection) {
    return collection instanceof Path<?> p
            && p.getModel() instanceof PluralAttribute<?, ?, ?>;
}

Type guard

static boolean memberOfSupported(Expression<?> coll) {
    return coll instanceof Path<?> p && p.getModel() instanceof PluralAttribute<?, ?, ?>;
}

Try / catch

try {
    p = cb.isMember(code, path);
} catch (SemanticException e) {
    if (e.getMessage().contains("plural path")) { /* switch to path.in(values) or a real collection attribute */ }
    else throw e;
}

Prevention

When it happens

Trigger: cb.isMember(code, root.get("order").get("customer")) where customer is @ManyToOne; cb.isMember(x, cb.literal(list)); cb.isMember(tag, subquery) or cb.isMember(tag, root.join("items")) — all are not plural-valued simple paths.

Common situations: Trying to express 'value is in this list' against a literal collection instead of using path.in(values); navigating to the wrong side of an association (entity side instead of the collection side); using the joined entity instead of the collection attribute in @OneToMany mappings.

Related errors


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