{"record":{"id":"3b555857494aa528","repo":"hibernate/hibernate-orm","slug":"cannot-compare-left-expression-of-type-s-with-r","errorCode":null,"errorMessage":"Cannot compare left expression of type '%s' with right expression of type '%s'","messagePattern":"Cannot compare left expression of type '(.+?)' with right expression of type '(.+?)'","errorType":"exception","errorClass":"SemanticException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/query/sqm/internal/TypecheckUtil.java","lineNumber":453,"sourceCode":"\n\t\t// allow comparing literal null to things\n\t\tif ( !( left instanceof SqmLiteralNull ) && !( right instanceof SqmLiteralNull ) ) {\n\t\t\tfinal var leftType = left.getExpressible();\n\t\t\tfinal var rightType = right.getExpressible();\n\t\t\tif ( leftType != null && rightType != null\n\t\t\t\t\t&& left.isEnum() && right.isEnum() ) {\n\t\t\t\t// this is needed by Hibernate Processor due to the weird\n\t\t\t\t// handling of enumerated types in the annotation processor\n\t\t\t\tif ( !Objects.equals( leftType.getTypeName(), rightType.getTypeName() ) ) {\n\t\t\t\t\tString.format(\n\t\t\t\t\t\t\t\"Cannot compare left expression of enumerated type '%s' with right expression of enumerated type '%s'\",\n\t\t\t\t\t\t\tleftType.getTypeName(),\n\t\t\t\t\t\t\trightType.getTypeName()\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if ( !areTypesComparable( leftType, rightType, bindingContext ) ) {\n\t\t\t\tthrow new SemanticException(\n\t\t\t\t\t\tString.format(\n\t\t\t\t\t\t\t\t\"Cannot compare left expression of type '%s' with right expression of type '%s'\",\n\t\t\t\t\t\t\t\tleftType.getTypeName(),\n\t\t\t\t\t\t\t\trightType.getTypeName()\n\t\t\t\t\t\t)\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * @see TypecheckUtil#assertComparable(Expression, Expression, BindingContext)\n\t */\n\tpublic static void assertAssignable(\n\t\t\t@Nullable String hqlString,\n\t\t\tSqmPath<?> targetPath, SqmTypedNode<?> expression,\n\t\t\tBindingContext bindingContext) {\n\t\t// allow assigning literal null to things","sourceCodeStart":435,"sourceCodeEnd":471,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/TypecheckUtil.java#L435-L471","documentation":"Thrown as SemanticException by TypecheckUtil.assertComparable when the two sides of a comparison predicate have types that are not comparable to each other (after literal-null is allowed and the enum special case is skipped). Hibernate type-checks HQL predicates during semantic analysis: comparing a String path to an Integer literal, a boolean to a number, or an entity to a scalar each fail here instead of producing broken SQL. Note the enum branch in the same method builds its mismatch message but currently does not throw — the non-enum branch is the one that raises this error.","triggerScenarios":"HQL 'where p.age = \"42\"' (numeric attribute vs string literal); comparing an enum attribute to a plain string literal of a different type; 'where p.active = 1' when active is boolean on a dialect where no implicit conversion applies; comparing an entity-valued path to a scalar; parameters bound with the wrong JavaType so the inferred types disagree.","commonSituations":"Query strings assembled from user input where numbers arrive as strings; porting native SQL predicates with implicit casts to HQL; enum attributes compared against their name() strings; dialect differences where an implicit conversion used to work.","solutions":["Fix the literal/parameter type to match the attribute: p.age = 42, not \"42\"","Bind parameters with the correct Java type: setParameter(\"age\", 42, Integer.class)","Cast explicitly when you need cross-type comparison: cast(p.age as string) = :v","For enums, compare against the enum literal or a properly typed enum parameter"],"exampleFix":"// before\nList<Person> r = session.createQuery(\"from Person p where p.age = :age\", Person.class)\n        .setParameter(\"age\", \"42\") // String vs int\n        .getResultList();\n// after\nList<Person> r = session.createQuery(\"from Person p where p.age = :age\", Person.class)\n        .setParameter(\"age\", 42, Integer.class)\n        .getResultList();","handlingStrategy":"try-catch","validationCode":"static Object coerceToAttributeType(Object raw, Class<?> attrType) {\n    if (raw == null) return null;\n    if (attrType.isInstance(raw)) return raw;\n    if (attrType == Integer.class || attrType == int.class) return Integer.valueOf(raw.toString());\n    if (attrType == Long.class || attrType == long.class) return Long.valueOf(raw.toString());\n    if (attrType == Boolean.class || attrType == boolean.class) return Boolean.valueOf(raw.toString());\n    throw new IllegalArgumentException(\"Cannot coerce \" + raw + \" to \" + attrType.getName());\n}\n// bind with: query.setParameter(name, coerceToAttributeType(value, attrType), attrType);","typeGuard":"static boolean typesComparable(Class<?> a, Class<?> b) {\n    if (a == null || b == null) return true;\n    if (a.isAssignableFrom(b) || b.isAssignableFrom(a)) return true;\n    return (a == String.class) == (b == String.class)\n        && (Number.class.isAssignableFrom(a)) == (Number.class.isAssignableFrom(b))\n        && (a == Boolean.class) == (b == Boolean.class);\n}","tryCatchPattern":"try {\n    return session.createQuery(hql, Person.class).setParameter(\"age\", value, Integer.class).getResultList();\n} catch (SemanticException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"Cannot compare\")) {\n        throw new IllegalArgumentException(\"Incomparable types in predicate of: \" + hql, e);\n    }\n    throw e;\n}","preventionTips":["Coerce user input to the attribute's Java type before binding","Always use the typed setParameter overload","Compare enums with enum literals or typed enum parameters, not strings","Cover each predicate with a smoke test against the mapped types"],"tags":["hibernate","hql","type-mismatch","comparison","semantic-analysis"],"backgroundTag":"hql-type-comparison-error","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}