{"id":"fdd88d367c255bfc","repo":"google/gson","slug":"numeric-values-must-be-finite-but-was-string","errorCode":null,"errorMessage":"Numeric values must be finite, but was \" + string","messagePattern":"Numeric values must be finite, but was \" \\+ string","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"gson/src/main/java/com/google/gson/stream/JsonWriter.java","lineNumber":646,"sourceCode":"   * @throws IllegalArgumentException if the value is NaN or Infinity and this writer is not {@link\n   *     #setStrictness(Strictness) lenient}; or if the {@code toString()} result is not a valid\n   *     JSON number.\n   */\n  @CanIgnoreReturnValue\n  public JsonWriter value(Number value) throws IOException {\n    if (value == null) {\n      return nullValue();\n    }\n\n    writeDeferredName();\n    String string = value.toString();\n    Class<? extends Number> numberClass = value.getClass();\n\n    if (!alwaysCreatesValidJsonNumber(numberClass)) {\n      // Validate that string is valid before writing it directly to JSON output\n      if (string.equals(\"-Infinity\") || string.equals(\"Infinity\") || string.equals(\"NaN\")) {\n        if (strictness != Strictness.LENIENT) {\n          throw new IllegalArgumentException(\"Numeric values must be finite, but was \" + string);\n        }\n      } else if (numberClass != Float.class\n          && numberClass != Double.class\n          && !VALID_JSON_NUMBER_PATTERN.matcher(string).matches()) {\n        throw new IllegalArgumentException(\n            \"String created by \" + numberClass + \" is not a valid JSON number: \" + string);\n      }\n    }\n\n    beforeValue();\n    out.append(string);\n    return this;\n  }\n\n  /**\n   * Encodes {@code null}.\n   *\n   * @return this writer.","sourceCodeStart":628,"sourceCodeEnd":664,"githubUrl":"https://github.com/google/gson/blob/8b8628c65699bc4421696183c62ae0c1b9b281dc/gson/src/main/java/com/google/gson/stream/JsonWriter.java#L628-L664","documentation":"Thrown by JsonWriter.value(Number) when the number's toString() is \"NaN\", \"Infinity\", or \"-Infinity\" and the writer is not in Strictness.LENIENT mode (default is LEGACY_STRICT). RFC 8259 JSON forbids these non-finite values, so Gson refuses to serialize them to keep output spec-compliant. The check only fires for Float/Double (and other types whose toString can yield those literals); integral types are trusted. Switching the writer to LENIENT lets NaN/Infinity through verbatim.","triggerScenarios":"Calling jsonWriter.value(Double.NaN), jsonWriter.value(Double.POSITIVE_INFINITY), jsonWriter.value(Float.NEGATIVE_INFINITY), or passing a Double/Float variable that is NaN/infinite while strictness is LEGACY_STRICT (default) or STRICT. Also reached indirectly when a TypeAdapter / JsonSerializer calls writer.value(d) on a computed double that overflowed to infinity (e.g. 1.0/0.0) or an unset field defaulting to NaN.","commonSituations":"Serializing scientific/financial data with division-by-zero producing infinity; logging or telemetry POJOs whose double fields are uninitialized to NaN; migrating from older Gson where setLenient(true) was used and the 2.11+ Strictness API reset it to LEGACY_STRICT; custom serializers that forward raw doubles without sanitizing; JSON-RPC/REST endpoints that must never emit non-finite numbers to JS clients (where NaN becomes null).","solutions":["Sanitize the value before writing: write null, 0, or a sentinel when !Double.isFinite(value), e.g. writer.value(Double.isFinite(d) ? d : null).","If emitting raw NaN/Infinity tokens is acceptable for your consumer, call writer.setStrictness(Strictness.LENIENT) before writing.","In a custom JsonSerializer, normalize the source field (replace NaN/infinity with null or a default) rather than forwarding it to JsonWriter.","Register a TypeAdapter for Double/Float that coerces non-finite values to null, so the fix is centralized."],"exampleFix":"// before\nDouble ratio = computeRatio(); // may be NaN or Infinity\njsonWriter.value(ratio);\n\n// after\nDouble ratio = computeRatio();\nif (ratio != null && Double.isFinite(ratio)) {\n  jsonWriter.value(ratio);\n} else {\n  jsonWriter.nullValue();\n}","handlingStrategy":"validation","validationCode":"// Run before writer.value(Number)\npublic static Number sanitizeNumber(Number n) {\n  if (n == null) return null;\n  if (n instanceof Double d && !Double.isFinite(d)) return null;\n  if (n instanceof Float f && !Float.isFinite(f)) return null;\n  return n;\n}\n// usage: writer.value(sanitizeNumber(d));","typeGuard":"import java.util.Objects;\n\n/** True only for finite Double/Float values safe for strict JSON. */\npublic static boolean isFiniteJsonNumber(Number n) {\n  if (n == null) return false;\n  if (n instanceof Double d) return Double.isFinite(d);\n  if (n instanceof Float f) return Float.isFinite(f);\n  return true; // integral / BigDecimal etc. are always finite\n}","tryCatchPattern":"try {\n  writer.value(d);\n} catch (IllegalArgumentException e) {\n  // non-finite number in strict mode; emit null instead\n  writer.nullValue();\n}","preventionTips":["Never let Double/Float fields default to NaN; initialize to null or 0.0.","Centralize numeric serialization through a TypeAdapter that coerces non-finite values.","Assert Double.isFinite(x) at the boundary where the value is computed (division, parsing).","If NaN/Infinity is semantically valid for your domain, set the writer to Strictness.LENIENT explicitly and document why."],"tags":["json","gson","serialization","numeric","strictness"],"analyzedSha":"8b8628c65699bc4421696183c62ae0c1b9b281dc","analyzedAt":"2026-08-04T19:12:22.202Z","schemaVersion":2}