hibernate/hibernate-orm · error · SemanticException

Operand of " + op.getOperatorSqlText() + " is of type '" + r

Error message

Operand of " + op.getOperatorSqlText() + " is of type '" + rightNodeType.getTypeName() + "' which is not a numeric type (it is not an instance of 'java.lang.Number' or 'java.time.TemporalAmount')

What it means

Hibernate 6's HQL/JPQL semantic analyzer type-checks every binary arithmetic expression via TypecheckUtil.assertOperable before SQL generation. For the '*' operator, when the left operand is a java.lang.Number, the right operand must also be a Number, or a java.time.TemporalAmount (so a duration can be scaled by a number). Any other right-hand type makes the query fail to compile with this SemanticException.

Source

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

				);
			}
		}
	}

	public static void assertOperable(SqmExpression<?> left, SqmExpression<?> right, BinaryArithmeticOperator op) {
		final var leftNodeType = left.getExpressible();
		final var rightNodeType = right.getExpressible();
		if ( leftNodeType != null && rightNodeType != null ) {
			final var leftJavaType = leftNodeType.getRelationalJavaType().getJavaTypeClass();
			final var rightJavaType = rightNodeType.getRelationalJavaType().getJavaTypeClass();
			if ( Number.class.isAssignableFrom( leftJavaType ) ) {
				// left operand is a number
				switch (op) {
					case MULTIPLY:
						if ( !Number.class.isAssignableFrom( rightJavaType )
								// we can scale a duration by a number
								&& !TemporalAmount.class.isAssignableFrom( rightJavaType ) ) {
							throw new SemanticException(
									"Operand of " + op.getOperatorSqlText() + " is of type '" + rightNodeType.getTypeName() +
											"' which is not a numeric type (it is not an instance of 'java.lang.Number' or 'java.time.TemporalAmount')"
							);
						}
						break;
					default:
						if ( !Number.class.isAssignableFrom( rightJavaType ) ) {
							throw new SemanticException(
									"Operand of " + op.getOperatorSqlText() + " is of type '" + rightNodeType.getTypeName() +
											"' which is not a numeric type (it is not an instance of 'java.lang.Number')"
							);
						}
						break;
				}
			}
			else if ( TemporalAmount.class.isAssignableFrom( leftJavaType ) ) {
				// left operand is a duration
				switch (op) {

View on GitHub (pinned to fad1729dce)

Solutions

  1. Reference the correct numeric attribute on the right side of '*'
  2. If the value lives in a String column, cast it: `e.amount * cast(o.code as double)`
  3. Store the value in a proper numeric column type instead of VARCHAR
  4. In dynamic query builders, verify via the JPA metamodel that the attribute's JavaType is assignable from Number before emitting '*'

Example fix

// before (o.sku is String)
select o.price * o.sku from Order o

// after
select o.price * cast(o.sku as double) from Order o
Defensive patterns

Strategy: try-catch

Validate before calling

// Before building the predicate, verify both operands are numeric
Metamodel mm = em.getMetamodel();
SingularAttribute<?, ?> left = mm.entity(Order.class).getSingularAttribute("price");
SingularAttribute<?, ?> right = mm.entity(Order.class).getSingularAttribute("sku");
if (!Number.class.isAssignableFrom(left.getJavaType())
        || !Number.class.isAssignableFrom(right.getJavaType())) {
    throw new IllegalArgumentException("'%' requires numeric operands");
}

Type guard

static boolean isNumericAttr(Attribute<?, ?> attr) {
    return Number.class.isAssignableFrom(attr.getJavaType());
}

Try / catch

try {
    return em.createQuery(hql, Long.class).getSingleResult();
} catch (org.hibernate.query.SemanticException e) {
    throw new InvalidQueryException("Type error in query (check operand types): " + hql, e);
}

Prevention

When it happens

Trigger: HQL like `select o.price * o.sku from Order o` where o.sku is a String; `where e.age * e.active > 1` (boolean right side); `e.total * e.createdDate`; any multiplication where the left side resolves to a numeric Java type but the right side resolves to String, Boolean, enum, UUID, or a date/time type (only TemporalAmount is exempted, and only for MULTIPLY).

Common situations: Numeric-looking codes stored in VARCHAR columns and used in arithmetic; typo'd property names in hand-written HQL; generic filter/sort builders that apply '*' to whatever attribute the caller passes; upgrading from Hibernate 5 or early 6.x where these type checks were absent or looser.

Related errors


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