hibernate/hibernate-orm · error · SemanticException

Cannot compare tuples of different lengths

Error message

Cannot compare tuples of different lengths

What it means

Thrown as SemanticException by TypecheckUtil.assertComparable when both sides of a comparison report a tuple length and the lengths differ. Hibernate supports tuple/row-value comparisons in HQL like '(p.firstName, p.lastName) = (:first, :last)'; the semantics of =, <, IN over tuples require equal arity on both sides, so '(a, b) = (x, y, z)' is rejected during semantic analysis before SQL generation.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/query/sqm/internal/TypecheckUtil.java:427

		return lhsEntity.isSubclassEntityName( rhsEntity.getEntityName() );
	}

	private static EntityPersister getEntityDescriptor(BindingContext bindingContext, String name) {
		return bindingContext.getMappingMetamodel()
				.getEntityDescriptor( bindingContext.getJpaMetamodel().qualifyImportableName( name ) );
	}

	/**
	 * @see TypecheckUtil#assertAssignable(String, SqmPath, SqmTypedNode, BindingContext)
	 */
	public static void assertComparable(Expression<?> x, Expression<?> y, BindingContext bindingContext) {
		final var left = (SqmExpression<?>) x;
		final var right = (SqmExpression<?>) y;
		final Integer leftTupleLength = left.getTupleLength();
		final Integer rightTupleLength = right.getTupleLength();
		if ( leftTupleLength != null && rightTupleLength != null
				&& leftTupleLength.intValue() != rightTupleLength.intValue() ) {
			throw new SemanticException( "Cannot compare tuples of different lengths" );
		}

		// SqmMemberOfPredicate is the only one allowing multivalued paths, its comparability is now evaluated in areTypesComparable
		// i.e. without calling this method, so we can check this here for other Predicates that do call this
		if ( left instanceof SqmPluralValuedSimplePath || right instanceof SqmPluralValuedSimplePath ) {
			throw new SemanticException( "Multivalued paths are only allowed for the 'member of' operator" );
		}

		// allow comparing literal null to things
		if ( !( left instanceof SqmLiteralNull ) && !( right instanceof SqmLiteralNull ) ) {
			final var leftType = left.getExpressible();
			final var rightType = right.getExpressible();
			if ( leftType != null && rightType != null
					&& left.isEnum() && right.isEnum() ) {
				// this is needed by Hibernate Processor due to the weird
				// handling of enumerated types in the annotation processor
				if ( !Objects.equals( leftType.getTypeName(), rightType.getTypeName() ) ) {
					String.format(

View on GitHub (pinned to fad1729dce)

Solutions

  1. Make both tuples the same length: list exactly the attributes of the composite key/id on each side
  2. For composite keys, compare the embedded path as a whole: 'where p.id = :id' with the embedded object bound
  3. When building tuples dynamically, assemble left and right sides from the same attribute list
  4. Prefer IN over = only with matching tuple arity

Example fix

// before
String hql = "from Order o where (o.billingCity, o.billingZip, o.billingCountry) = (:city, :zip)";
// after
String hql = "from Order o where (o.billingCity, o.billingZip) = (:city, :zip)";
Defensive patterns

Strategy: try-catch

Validate before calling

static void assertSameTupleLength(List<String> leftAttrs, List<String> rightAttrs) {
    if (leftAttrs.size() != rightAttrs.size()) {
        throw new IllegalArgumentException("Tuple arity mismatch: " + leftAttrs.size() + " vs " + rightAttrs.size());
    }
}

Try / catch

try {
    return session.createQuery(hql, Person.class).getResultList();
} catch (SemanticException e) {
    if (e.getMessage() != null && e.getMessage().contains("tuples of different lengths")) {
        throw new IllegalArgumentException("Tuple comparison arity mismatch in: " + hql, e);
    }
    throw e;
}

Prevention

When it happens

Trigger: HQL tuple comparisons with different arity: 'where (p.first, p.last) = (:first, :last, :middle)'; comparing an embeddable (treated as a tuple) against a tuple literal of a different number of elements; copying a composite-key equality predicate from another entity whose id has a different number of columns; tuple IN lists whose element arity differs from the left-hand tuple.

Common situations: Composite key / embeddable equality predicates written by hand; queries ported between entities with similar-sounding keys but different column counts; dynamic tuple builders that append filter columns conditionally to only one side.

Related errors


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