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
- For string concatenation use `concat(a, b, c)` or `a || b` instead of '+'
- Cast string operands to a numeric type: `e.salary + cast(e.bonusCode as double)`
- For number-with-duration math, put the number on the LEFT and use '*' (`2 * e.duration`), or keep duration + duration
- 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
- Never use '+' for string concatenation in HQL/JPQL — always concat() or ||
- Remember number + duration is invalid; scale durations as `n * duration`
- Add an integration test per stored query so operand-type regressions surface at build time
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
- Operand of " + op.getOperatorSqlText() + " is of type '" + r
- Operand of " + op.getOperatorSqlText() + " is of type '" + r
- Operand of " + op.getOperatorSqlText() + " is of type '" + l
- Operand of " + op.getOperatorSqlText() + " is of type '" + l
- Operand of " + op.getOperatorSqlText() + " is of type '" + r
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/e4b0c176d08faef0.
Report an issue: GitHub.