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')

What it means

TypecheckUtil.assertOperands checks binary arithmetic: for every operator except MULTIPLY (+, -, /, %), when the left operand is a Number the right operand must also be a Number. Unlike '*', no TemporalAmount exemption exists, so `1 + duration` is rejected too. The query fails at semantic-analysis time with this SemanticException.

Source

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

		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) {
					case ADD:
					case SUBTRACT:
						// we can add/subtract durations
						if ( !TemporalAmount.class.isAssignableFrom( rightJavaType ) ) {
							throw new SemanticException(
									"Operand of " + op.getOperatorSqlText() + " is of type '" + rightNodeType.getTypeName() +
											"' which is not a temporal amount (it is not an instance of 'java.time.TemporalAmount')"
							);

View on GitHub (pinned to fad1729dce)

Solutions

  1. For string concatenation use `concat(a, b, c)` or `a || b` instead of '+'
  2. Cast string operands to a numeric type: `e.salary + cast(e.bonusCode as double)`
  3. For number-with-duration math, put the number on the LEFT and use '*' (`2 * e.duration`), or keep duration + duration
  4. Fix the attribute mapping so arithmetic columns are numeric types

Example fix

// before (e.tax is String)
select e.amount + e.tax from Invoice e

// after
select e.amount + cast(e.tax as double) from Invoice e
Defensive patterns

Strategy: validation

Validate before calling

// Dynamic '+': only emit arithmetic when both sides are Number, else use concat
Object l = attrJavaType(leftAttr), r = attrJavaType(rightAttr);
String expr = (Number.class.isAssignableFrom((Class<?>) l)
            && Number.class.isAssignableFrom((Class<?>) r))
    ? leftAttr + " + " + rightAttr
    : "concat(" + leftAttr + ", ' ', " + rightAttr + ")";

Type guard

static boolean bothNumeric(Class<?> a, Class<?> b) {
    return Number.class.isAssignableFrom(a) && Number.class.isAssignableFrom(b);
}

Try / catch

try {
    em.createQuery(hql).executeUpdate();
} catch (org.hibernate.query.SemanticException e) {
    log.error("Operand type mismatch in + expression: {}", hql, e);
    throw e;
}

Prevention

When it happens

Trigger: HQL `e.salary + e.bonusCode` (String right side); `e.qty - e.active` (Boolean); `e.total / e.name`; `1 + x.day` or `5 + e.duration` (adding a number to a duration — the duration must be the LEFT operand for +, and only durations may be added to durations).

Common situations: Trying SQL-style string concatenation with '+' (HQL/JPQL requires concat() or ||); numeric data kept in text columns; adding numbers to `n day` style duration literals; generic query builders applying +/- to untyped attributes; stricter checks appearing when upgrading to Hibernate 6.3+.

Related errors


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