{"record":{"id":"cadb2abcc371c4cb","repo":"hibernate/hibernate-orm","slug":"cannot-coerce-double-value-s-to-byte-not-a-wh","errorCode":null,"errorMessage":"Cannot coerce Double value `%s` to Byte : not a whole number","messagePattern":"Cannot coerce Double value `(.+?)` to Byte : not a whole number","errorType":"exception","errorClass":"CoercionException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CoercionHelper.java","lineNumber":96,"sourceCode":"\t\t\t);\n\t\t}\n\n\t\tif ( value < Byte.MIN_VALUE ) {\n\t\t\tthrow new CoercionException(\n\t\t\t\t\tString.format(\n\t\t\t\t\t\t\tLocale.ROOT,\n\t\t\t\t\t\t\t\"Cannot coerce Long value `%s` to Byte : underflow\",\n\t\t\t\t\t\t\tvalue\n\t\t\t\t\t)\n\t\t\t);\n\t\t}\n\n\t\treturn value.byteValue();\n\t}\n\n\tpublic static Byte toByte(Double value) {\n\t\tif ( ! isWholeNumber( value ) ) {\n\t\t\tthrow new CoercionException(\n\t\t\t\t\tString.format(\n\t\t\t\t\t\t\tLocale.ROOT,\n\t\t\t\t\t\t\t\"Cannot coerce Double value `%s` to Byte : not a whole number\",\n\t\t\t\t\t\t\tvalue\n\t\t\t\t\t)\n\t\t\t);\n\t\t}\n\n\t\tif ( value > Byte.MAX_VALUE ) {\n\t\t\tthrow new CoercionException(\n\t\t\t\t\tString.format(\n\t\t\t\t\t\t\tLocale.ROOT,\n\t\t\t\t\t\t\t\"Cannot coerce Double value `%s` to Byte : overflow\",\n\t\t\t\t\t\t\tvalue\n\t\t\t\t\t)\n\t\t\t);\n\t\t}\n","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CoercionHelper.java#L78-L114","documentation":"Hibernate throws this CoercionException when a Double must be coerced to Byte but has a fractional part. CoercionHelper.toByte(Double) first checks isWholeNumber(value); any value like 3.14 fails before the range checks run. It is reached through ByteJavaType.coerce when a Double value is assigned to or bound against a Byte-mapped attribute. Hibernate treats fractional-to-integral coercion as lossy and rejects it rather than truncating.","triggerScenarios":"`ByteJavaType.coerce(3.14)` — e.g. `entity.setRatio(computedDouble)` on a Byte/byte attribute, or `setParameter(\"b\", 2.5)` against a Byte-typed path, or HQL/Criteria arithmetic (averages, division) producing Double results compared/assigned to a Byte attribute.","commonSituations":"Computed metrics (averages, percentages) stored into tinyint-mapped Byte fields; JSON numbers with decimals (Jackson deserializes as Double) copied onto Byte properties; unit bugs (storing 0.5 kg in a field meant for whole grams); switching an attribute from Double to Byte during a model refactor while old writers still send fractions.","solutions":["Fix the producer: round/quantize before assignment (`(byte) Math.round(d)`) only if truncation semantics are acceptable, otherwise store the value in a Double/BigDecimal-typed column.","Change the attribute type to Double/BigDecimal if fractional values are legitimate.","Sanitize incoming data at the boundary: reject or round non-whole numbers before they reach the entity.","Re-check any HQL/Criteria expressions feeding Byte paths; cast or round in the query (`floor()`, `round()`) as appropriate."],"exampleFix":"// before\ndouble avg = readings.stream().mapToDouble(r -> r.value).average().orElse(0);\nsensor.setCalibration(avg);      // Byte field -> \"not a whole number\"\n\n// after\nsensor.setCalibration((byte) Math.round(avg));\n// or make calibration a Double column if fractions matter","handlingStrategy":"validation","validationCode":"Double d = computed;\nif (d != Math.rint(d) || Double.isNaN(d) || Double.isInfinite(d)) {\n    throw new IllegalArgumentException(\"value must be a whole number: \" + d);\n}\nentity.setByteField((byte) (long) (double) d); // after additional range check","typeGuard":"static boolean isWhole(Double d) { return d != null && !d.isNaN() && !d.isInfinite() && d == Math.rint(d); }","tryCatchPattern":"try { session.persist(e); } catch (CoercionException ex) { /* convert to domain validation error, surface to caller */ }","preventionTips":["Decide explicitly: either the column stores fractions (use Double/BigDecimal mapping) or the producer must quantize before writing.","Round in one canonical place (service layer), not ad hoc at call sites.","Reject NaN/Infinity early — they also fail whole-number checks downstream.","Cover fractional inputs in contract tests for integral endpoints."],"tags":["hibernate","type-coercion","byte","double","fractional"],"backgroundTag":"fractional-to-integer-coercion","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}