hibernate/hibernate-orm · error · IllegalArgumentException

Multi-select expressions have duplicate alias '{}'

Error message

Multi-select expressions have duplicate alias '{}'

What it means

checkMultiselect also enforces alias uniqueness: among the selection items passed to multiselect, any non-empty alias (selection.alias(...)) may occur at most once. A repeated alias would make result-by-name mapping ambiguous, so Hibernate throws IllegalArgumentException naming the duplicated alias.

Source

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

	private void checkMultiselect(List<? extends Selection<?>> selections) {
		final HashSet<String> aliases = new HashSet<>( determineProperSizing( selections.size() ) );
		for ( var selection : selections ) {
			if ( selection.isCompoundSelection() ) {
				final Class<?> javaType = selection.getJavaType();
				if ( javaType.isArray() ) {
					throw new IllegalArgumentException(
							"Selection item in a multi-select cannot contain compound array-valued elements"
					);
				}
				if ( Tuple.class.isAssignableFrom( javaType ) ) {
					throw new IllegalArgumentException(
							"Selection item in a multi-select cannot contain compound tuple-valued elements"
					);
				}
			}
			final String alias = selection.getAlias();
			if ( StringHelper.isNotEmpty( alias ) && !aliases.add( alias ) ) {
				throw new IllegalArgumentException( "Multi-select expressions have duplicate alias '" + alias + "'" );
			}
		}
	}

	@Nonnull
	@Override
	public <N extends Number> SqmExpression<Double> avg(@Nonnull Expression<N> argument) {
		return getFunctionDescriptor( "avg" ).generateSqmExpression(
				(SqmTypedNode<?>) argument,
				null,
				queryEngine
		);
	}

	@Nonnull
	@Override
	@SuppressWarnings("unchecked")
	public <N extends Number> SqmExpression<N> sum(@Nonnull Expression<N> argument) {

View on GitHub (pinned to fad1729dce)

Solutions

  1. Give every aliased item a unique alias (id1/id2, empName/mgrName).
  2. If aliases are generated from a column catalog, de-duplicate by appending an index or the path prefix.
  3. Drop aliases on items that don't need name-based access.

Example fix

// before
query.multiselect(e.get("id").alias("id"), m.get("id").alias("id")); // duplicate alias 'id'

// after
query.multiselect(e.get("id").alias("empId"), m.get("id").alias("mgrId"));
Defensive patterns

Strategy: validation

Validate before calling

boolean aliasesUnique(List<? extends Selection<?>> items) {
    Set<String> seen = new HashSet<>();
    for (Selection<?> s : items) {
        String a = s.getAlias();
        if (a != null && !a.isEmpty() && !seen.add(a)) return false;
    }
    return true;
}
if (!aliasesUnique(selections)) throw new IllegalArgumentException("duplicate alias in multiselect");

Type guard

static Optional<String> firstDuplicateAlias(List<? extends Selection<?>> items) {
    Set<String> seen = new HashSet<>();
    return items.stream().map(Selection::getAlias)
            .filter(a -> a != null && !a.isEmpty())
            .filter(a -> !seen.add(a)).findFirst();
}

Try / catch

try {
    query.multiselect(selections);
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("duplicate alias")) { /* de-duplicate: alias("empId"), alias("mgrId") */ }
    else throw e;
}

Prevention

When it happens

Trigger: query.multiselect(root.get("id").alias("id"), otherRoot.get("id").alias("id")); self-joins where both sides expose the same attribute alias; dynamic projections assembled from column catalogs that reuse display names ('name', 'value'); expressions that inherit an alias from a reused Selection instance.

Common situations: Generic grid/report builders that map UI column keys straight to aliases and collide on joins; entity self-joins (employee/manager both aliasing 'name'); copy-pasted selection lists where one alias is forgotten to be renamed.

Related errors


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