hibernate/hibernate-orm · error · SemanticException
Operand of " + op.getOperatorSqlText() + " is of type '" + l
Error message
Operand of " + op.getOperatorSqlText() + " is of type '" + leftNodeType.getTypeName() + "' which is not a numeric type (it is not an instance of 'java.lang.Number', 'java.time.Temporal', or 'java.time.TemporalAmount')
What it means
This is the catch-all branch of assertOperable: the LEFT operand of a binary arithmetic operator must be a Number, Temporal, java.util.Date, TemporalAmount, or numeric array. Strings, booleans, enums, UUIDs, byte[], and non-numeric arrays all fall through and the query fails with this SemanticException.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/query/sqm/internal/TypecheckUtil.java:595
break;
default:
throw new SemanticException(
"Operand of " + op.getOperatorSqlText() + " is of type '" + leftNodeType.getTypeName() +
"' which is not a numeric type (it is not an instance of 'java.lang.Number')"
);
}
}
else if ( isNumberArray( leftNodeType ) ) {
// left operand is a number
if ( !isNumberArray( rightNodeType ) ) {
throw new SemanticException(
"Operand of " + op.getOperatorSqlText() + " is of type '" + rightNodeType.getTypeName() +
"' which is not a numeric array type" + " (it is not an instance of 'java.lang.Number[]')"
);
}
}
else {
throw new SemanticException(
"Operand of " + op.getOperatorSqlText()
+ " is of type '" + leftNodeType.getTypeName() + "' which is not a numeric type"
+ " (it is not an instance of 'java.lang.Number', 'java.time.Temporal', or 'java.time.TemporalAmount')"
);
}
}
}
public static boolean isNumberArray(@Nullable SqmExpressible<?> expressible) {
if ( expressible != null ) {
final var domainType = expressible.getSqmType();
if ( domainType != null ) {
return domainType instanceof BasicPluralType<?, ?> basicPluralType
&& Number.class.isAssignableFrom( basicPluralType.getElementType().getJavaType() );
}
}
return false;
}View on GitHub (pinned to fad1729dce)
Solutions
- Use `concat(e.firstName, ' ', e.lastName)` or `e.firstName || ' ' || e.lastName` for strings
- Cast genuinely numeric content stored as text: `cast(e.code as integer) + 1`
- Remove arithmetic from boolean/enum/UUID attributes; express those conditions as comparisons
- For enums use `cast(e.status as integer)` when you need the ordinal numerically
Example fix
// before where e.firstName + ' ' + e.lastName = :fullName // after where concat(e.firstName, ' ', e.lastName) = :fullName
Defensive patterns
Strategy: try-catch
Validate before calling
// Generic search filter: only allow 'like'/'concat' paths on non-numeric fields
SingularAttribute<?, ?> attr = mm.entity(Person.class).getSingularAttribute(field);
if (!String.class.equals(attr.getJavaType())
&& !Number.class.isAssignableFrom(attr.getJavaType())) {
throw new IllegalArgumentException("Arithmetic unsupported for field " + field);
} Type guard
static boolean supportsArithmetic(Class<?> c) {
return Number.class.isAssignableFrom(c)
|| java.time.temporal.TemporalAmount.class.isAssignableFrom(c)
|| java.time.temporal.Temporal.class.isAssignableFrom(c)
|| java.util.Date.class.isAssignableFrom(c);
} Try / catch
try {
return em.createQuery(hql).getResultList();
} catch (org.hibernate.query.SemanticException e) {
throw new QueryBuildException(
"Left operand not arithmetic-capable — use concat() for strings: " + hql, e);
} Prevention
- Use concat() or || for strings; '+' is never string concatenation in HQL
- When upgrading to Hibernate 6.3+, run your full query catalog in CI to catch newly-strict checks
- Type-check dynamic HQL fragments against the JPA metamodel before executing them
When it happens
Trigger: `e.firstName + ' ' + e.lastName` (string concatenation with '+'); `e.active + 1` (Boolean); `e.status + 1` (enum); `e.tags + e.labels` (String[] + String[]); arithmetic on a UUID or JSON attribute.
Common situations: The single most common hit: SQL habits where '+' concatenates strings — JPQL/HQL require concat() or ||; generic search builders arithmetic-ing whatever field was supplied; upgrading to Hibernate 6.3+ (HHH-15345 era) where these checks became strict and previously-tolerated queries now fail.
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 '" + r
- Ordinal parameter labels start from '?%s' (ordinal parameter
- Gap between '?%s' and '?%s' in ordinal parameter labels [%s]
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/79698a505909051e.
Report an issue: GitHub.