{"record":{"id":"4210545cd9855d5f","repo":"elastic/elasticsearch","slug":"value-stringvalue-has-a-decimal-part","errorCode":null,"errorMessage":"Value [${stringValue}] has a decimal part","messagePattern":"Value \\[(.+?)\\] has a decimal part","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"libs/x-content/src/main/java/org/elasticsearch/xcontent/support/AbstractXContentParser.java","lineNumber":237,"sourceCode":"        try {\n            final BigDecimal bigDecimalValue = new BigDecimal(stringValue);\n            // long can have a maximum of 19 digits - any more than that cannot be a long\n            // the scale is stored as the negation, so negative scale -> big number\n            if (bigDecimalValue.scale() < -19) {\n                throw new IllegalArgumentException(\"Value [\" + stringValue + \"] is out of range for a long\");\n            }\n            // large scale -> very small number\n            if (bigDecimalValue.scale() > 19) {\n                if (coerce) {\n                    bigIntegerValue = BigInteger.ZERO;\n                } else {\n                    throw new ArithmeticException(\"Number has a decimal part\");\n                }\n            } else {\n                bigIntegerValue = coerce ? bigDecimalValue.toBigInteger() : bigDecimalValue.toBigIntegerExact();\n            }\n        } catch (ArithmeticException e) {\n            throw new IllegalArgumentException(\"Value [\" + stringValue + \"] has a decimal part\");\n        } catch (NumberFormatException e) {\n            throw new IllegalArgumentException(\"For input string: \\\"\" + stringValue + \"\\\"\");\n        }\n\n        if (bigIntegerValue.compareTo(LONG_MAX_VALUE_AS_BIGINTEGER) > 0 || bigIntegerValue.compareTo(LONG_MIN_VALUE_AS_BIGINTEGER) < 0) {\n            throw new IllegalArgumentException(\"Value [\" + stringValue + \"] is out of range for a long\");\n        }\n\n        assert bigIntegerValue.longValueExact() <= Long.MAX_VALUE; // asserting that no ArithmeticException is thrown\n        return bigIntegerValue.longValue();\n    }\n\n    @Override\n    public long longValue() throws IOException {\n        return longValue(DEFAULT_NUMBER_COERCE_POLICY);\n    }\n\n    @Override","sourceCodeStart":219,"sourceCodeEnd":255,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/libs/x-content/src/main/java/org/elasticsearch/xcontent/support/AbstractXContentParser.java#L219-L255","documentation":"Thrown by AbstractXContentParser.toLong when coerce=false and the parsed BigDecimal has a fractional part that cannot be losslessly converted to an integer. The ArithmeticException from toBigIntegerExact() (or the explicit scale>19 branch) is caught and re-thrown as this IllegalArgumentException. It enforces strict (non-coercing) long parsing semantics.","triggerScenarios":"Parsing a string like \"1.5\", \"3.14\", or \"0.001\" into a long with coerce=false (the strict policy). Reached via longValue(false) on a VALUE_STRING token, or when a long-mapped field receives a decimal value and the index-time coerce setting is disabled.","commonSituations":"Mappings with \"coerce\": false on a long field receiving float input. Reading JSON where a numeric field is serialized with a decimal point by a producer that treats all numbers as doubles. ES|QL or runtime field evaluation casting decimal strings to long without coercion.","solutions":["Enable coercion on the field mapping: set \"coerce\": true so fractional parts are truncated toward zero.","Fix the producer to emit integer values (round/truncate before sending).","Change the field type to double/scaled_float if fractional precision is genuinely needed.","If reading manually via the parser, call longValue(true) to allow truncation."],"exampleFix":"// before: strict long field rejects \"12.99\"\nPUT /idx/_mapping { \"properties\": { \"qty\": { \"type\": \"long\", \"coerce\": false } } }\n// after: allow truncation\nPUT /idx/_mapping { \"properties\": { \"qty\": { \"type\": \"long\", \"coerce\": true } } }","handlingStrategy":"validation","validationCode":"static boolean isStrictLong(String s) {\n  if (s == null) return false;\n  try { Long.parseLong(s); return true; }\n  catch (NumberFormatException e) { return false; }\n}","typeGuard":null,"tryCatchPattern":"try { parser.longValue(false); }\ncatch (IllegalArgumentException e) {\n  if (e.getMessage().contains(\"decimal part\")) { /* coerce or reject */ }\n  else throw e;\n}","preventionTips":["Either enable coerce on long fields or guarantee integer-only producers.","Use longValue(true) when truncation is acceptable.","Round/truncate decimal sources before indexing into long fields."],"tags":["xcontent","parsing","numeric","long","coerce"],"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T06:17:24.410Z"}