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
- Reference the correct numeric attribute on the right side of '*'
- If the value lives in a String column, cast it: `e.amount * cast(o.code as double)`
- Store the value in a proper numeric column type instead of VARCHAR
- 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
- Compile all named HQL queries at startup (createNamedQuery on boot) so type errors fail fast in CI, not in production
- Keep numeric data in numeric column types; never do arithmetic on VARCHAR columns
- In generic filter builders, whitelist arithmetic to attributes whose metamodel JavaType is Number
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
- 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/845f0de5b71c1958.
Report an issue: GitHub.