{"record":{"id":"79698a505909051e","repo":"hibernate/hibernate-orm","slug":"operand-of-op-getoperatorsqltext-is-of-t-79698a","errorCode":null,"errorMessage":"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')","messagePattern":"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'\\)","errorType":"exception","errorClass":"SemanticException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/query/sqm/internal/TypecheckUtil.java","lineNumber":595,"sourceCode":"\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tthrow new SemanticException(\n\t\t\t\t\t\t\t\t\"Operand of \" + op.getOperatorSqlText() + \" is of type '\" + leftNodeType.getTypeName() +\n\t\t\t\t\t\t\t\t\t\t\"' which is not a numeric type (it is not an instance of 'java.lang.Number')\"\n\t\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if ( isNumberArray( leftNodeType ) ) {\n\t\t\t\t// left operand is a number\n\t\t\t\tif ( !isNumberArray( rightNodeType ) ) {\n\t\t\t\t\tthrow new SemanticException(\n\t\t\t\t\t\t\t\"Operand of \" + op.getOperatorSqlText() + \" is of type '\" + rightNodeType.getTypeName() +\n\t\t\t\t\t\t\t\t\t\"' which is not a numeric array type\" + \" (it is not an instance of 'java.lang.Number[]')\"\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow new SemanticException(\n\t\t\t\t\t\t\"Operand of \" + op.getOperatorSqlText()\n\t\t\t\t\t\t\t\t+ \" is of type '\" + leftNodeType.getTypeName() + \"' which is not a numeric type\"\n\t\t\t\t\t\t\t\t+ \" (it is not an instance of 'java.lang.Number', 'java.time.Temporal', or 'java.time.TemporalAmount')\"\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static boolean isNumberArray(@Nullable SqmExpressible<?> expressible) {\n\t\tif ( expressible != null ) {\n\t\t\tfinal var domainType = expressible.getSqmType();\n\t\t\tif ( domainType != null ) {\n\t\t\t\treturn domainType instanceof BasicPluralType<?, ?> basicPluralType\n\t\t\t\t\t&& Number.class.isAssignableFrom( basicPluralType.getElementType().getJavaType() );\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}","sourceCodeStart":577,"sourceCodeEnd":613,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/TypecheckUtil.java#L577-L613","documentation":"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.","triggerScenarios":"`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.","commonSituations":"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.","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"],"exampleFix":"// before\nwhere e.firstName + ' ' + e.lastName = :fullName\n\n// after\nwhere concat(e.firstName, ' ', e.lastName) = :fullName","handlingStrategy":"try-catch","validationCode":"// Generic search filter: only allow 'like'/'concat' paths on non-numeric fields\nSingularAttribute<?, ?> attr = mm.entity(Person.class).getSingularAttribute(field);\nif (!String.class.equals(attr.getJavaType())\n        && !Number.class.isAssignableFrom(attr.getJavaType())) {\n    throw new IllegalArgumentException(\"Arithmetic unsupported for field \" + field);\n}","typeGuard":"static boolean supportsArithmetic(Class<?> c) {\n    return Number.class.isAssignableFrom(c)\n        || java.time.temporal.TemporalAmount.class.isAssignableFrom(c)\n        || java.time.temporal.Temporal.class.isAssignableFrom(c)\n        || java.util.Date.class.isAssignableFrom(c);\n}","tryCatchPattern":"try {\n    return em.createQuery(hql).getResultList();\n} catch (org.hibernate.query.SemanticException e) {\n    throw new QueryBuildException(\n        \"Left operand not arithmetic-capable — use concat() for strings: \" + hql, e);\n}","preventionTips":["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"],"tags":["hql","jpql","string-concatenation","type-checking","hibernate-6","migration"],"backgroundTag":"query-operand-type-mismatch","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}