hibernate/hibernate-orm · error · SemanticException
Cannot assign expression of type '%s' to target path '%s' of
Error message
Cannot assign expression of type '%s' to target path '%s' of type '%s'
What it means
Thrown as SemanticException by TypecheckUtil.assertAssignable when an expression being assigned to a target path has a type that is not assignable to the path's type. This guards assignment positions — primarily the SET clause of HQL UPDATE statements and criteria update cb.set(path, value) — so 'update Person p set p.age = ...' rejects an expression whose type cannot become the attribute's type (String into int, wrong enum, entity into scalar).
Source
Thrown at hibernate-core/src/main/java/org/hibernate/query/sqm/internal/TypecheckUtil.java:490
if ( expression instanceof SqmLiteralNull ) {
// TODO: check that the target path is nullable
}
else {
final var targetType = targetPath.getNodeType();
final var expressionType = expression.getNodeType();
if ( targetType != null && expressionType != null && targetPath.isEnum() ) {
// this is needed by Hibernate Processor due to the weird
// handling of enumerated types in the annotation processor
if ( !Objects.equals( targetType.getTypeName(), expressionType.getTypeName() ) ) {
String.format(
"Cannot compare left expression of enumerated type '%s' with right expression of enumerated type '%s'",
targetType.getTypeName(),
expressionType.getTypeName()
);
}
}
else if ( !isTypeAssignable( targetType, expressionType, bindingContext) ) {
throw new SemanticException(
String.format(
"Cannot assign expression of type '%s' to target path '%s' of type '%s'",
expressionType.getTypeName(),
targetPath.toHqlString(),
targetType.getTypeName()
),
hqlString,
null
);
}
}
}
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();View on GitHub (pinned to fad1729dce)
Solutions
- Convert the value to the attribute's type before binding: setParameter("age", 42, Integer.class)
- Cast inside HQL when necessary: set p.age = cast(:v as int)
- Fix the update statement to use a correctly typed expression (numeric literal, enum literal)
- For criteria updates, use the typed set(Path<Y>, Y) overload so the compiler checks the value type
Example fix
// before
int n = session.createMutationQuery("update Person p set p.age = :age")
.setParameter("age", "42") // String into int attribute
.executeUpdate();
// after
int n = session.createMutationQuery("update Person p set p.age = :age")
.setParameter("age", 42, Integer.class)
.executeUpdate(); Defensive patterns
Strategy: validation
Validate before calling
static Object checkAssignable(ManagedType<?> type, String attr, Object value) {
Class<?> javaType = ((SingularAttribute<?, ?>) type.getAttribute(attr)).getJavaType();
if (value != null && !javaType.isInstance(value)) {
throw new IllegalArgumentException(
"Value " + value + " (" + value.getClass().getSimpleName() + ") not assignable to "
+ attr + " of type " + javaType.getSimpleName());
}
return value;
}
// use before building the update: cb.set(root.get(attr), (Comparable) checkAssignable(type, attr, value)); Type guard
static <Y> Y guardSetType(Class<Y> attrType, Object value) {
if (value == null || attrType.isInstance(value)) {
return attrType.cast(value);
}
throw new IllegalArgumentException("Expected " + attrType.getSimpleName() + " but got " + value.getClass().getSimpleName());
} Try / catch
try {
int n = session.createMutationQuery("update Person p set p.age = :age")
.setParameter("age", 42, Integer.class).executeUpdate();
} catch (SemanticException e) {
if (e.getMessage() != null && e.getMessage().contains("Cannot assign")) {
throw new IllegalArgumentException("SET value type does not match attribute type", e);
}
throw e;
} Prevention
- Convert DTO/map input to the attribute's Java type before building the update
- Use the typed set(Path<Y>, Y) criteria overload so the compiler enforces types
- Keep bulk update statements next to the entity mapping so type changes get caught
- Bind update parameters with explicit types: setParameter(name, value, Integer.class)
When it happens
Trigger: HQL 'update Person p set p.age = "42"' (string expression into numeric attribute); 'set p.status = "ACTIVE"' where status is an enum compared against a plain string; criteria update cb.set(root.get("age"), someString); dynamic update builders copying values from a DTO/map with stringified numbers into typed attributes.
Common situations: Bulk update endpoints accepting JSON where all values arrive as strings; ETL/import scripts doing string-based updates; copy-pasting SET clauses between entities with different attribute types; refactoring an attribute's type without updating bulk update statements.
Related errors
- Cannot compare left expression of type '%s' with right expre
- Parameter %d of function '%s()' has type '%s', but argument
- Start and stop parameters of function '%s()' must be of the
- Step parameter of function '%s()' is of type '%s', but must
- Step parameter of function '%s()' is of type '%s', but must
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/4fa4fb0402aa19bd.
Report an issue: GitHub.