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

  1. Convert the value to the attribute's type before binding: setParameter("age", 42, Integer.class)
  2. Cast inside HQL when necessary: set p.age = cast(:v as int)
  3. Fix the update statement to use a correctly typed expression (numeric literal, enum literal)
  4. 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

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


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