google/gson · error · IllegalArgumentException
String created by {} is not a valid JSON number: {}
Error message
String created by {} is not a valid JSON number: {} What it means
Thrown by JsonWriter.value(Number) (JsonWriter.java:648-653) when the Number subclass is not one of the always-valid types (Integer, Long, Byte, Short, BigDecimal, BigInteger, AtomicInteger, AtomicLong) and its toString() does not match the VALID_JSON_NUMBER_PATTERN regex (-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][-+]?[0-9]+)?). This catches custom Number implementations whose string form would produce invalid JSON (leading zeros, hex, locale commas, trailing letters, etc.).
Source
Thrown at gson/src/main/java/com/google/gson/stream/JsonWriter.java:651
public JsonWriter value(Number value) throws IOException {
if (value == null) {
return nullValue();
}
writeDeferredName();
String string = value.toString();
Class<? extends Number> numberClass = value.getClass();
if (!alwaysCreatesValidJsonNumber(numberClass)) {
// Validate that string is valid before writing it directly to JSON output
if (string.equals("-Infinity") || string.equals("Infinity") || string.equals("NaN")) {
if (strictness != Strictness.LENIENT) {
throw new IllegalArgumentException("Numeric values must be finite, but was " + string);
}
} else if (numberClass != Float.class
&& numberClass != Double.class
&& !VALID_JSON_NUMBER_PATTERN.matcher(string).matches()) {
throw new IllegalArgumentException(
"String created by " + numberClass + " is not a valid JSON number: " + string);
}
}
beforeValue();
out.append(string);
return this;
}
/**
* Encodes {@code null}.
*
* @return this writer.
*/
@CanIgnoreReturnValue
public JsonWriter nullValue() throws IOException {
if (deferredName != null) {
if (serializeNulls) {View on GitHub (pinned to 310ac341f2)
Solutions
- Convert the custom Number to a known-good type before writing: writer.value(number.doubleValue()) or writer.value(new BigDecimal(number.toString())).
- If you control the Number subclass, ensure toString() returns a strictly RFC-8259-compliant number.
- Validate the string against the JSON number grammar before calling value(Number).
- For non-finite cases, handle separately (see error 177).
Example fix
// before writer.value(customNumber); // throws if toString() is "0x1A" // after writer.value(new BigDecimal(customNumber.toString()));
Defensive patterns
Strategy: validation
Validate before calling
String s = number.toString();
boolean valid = s.matches("-?(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?(?:[eE][-+]?[0-9]+)?");
if (valid) {
writer.value(number);
} else {
writer.value(new BigDecimal(s));
} Type guard
private static final Pattern JSON_NUMBER =
Pattern.compile("-?(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?(?:[eE][-+]?[0-9]+)?");
static boolean isValidJsonNumberString(String s) {
return JSON_NUMBER.matcher(s).matches();
} Try / catch
try {
writer.value(number);
} catch (IllegalArgumentException e) {
writer.value(number.doubleValue()); // fall back to a known-good Number
} Prevention
- Convert custom Number subclasses to BigDecimal/Double before writing.
- If you own the Number subclass, ensure toString() is RFC-8259 compliant.
- Add tests with locale-formatted and hex strings to catch regressions.
When it happens
Trigger: Passing a custom Number subclass (e.g. a LazyParsedNumber or domain-specific Number) whose toString() returns something like "0x1A", "1,000", "1.0e", "+5", or "01". Also triggered by a Number that returns "" or whitespace.
Common situations: Custom Number types for performance (lazy parsing, cached strings); third-party libraries exposing their own Number subclasses; locale-aware toString implementations; numbers carrying metadata in their string form.
Related errors
- Numeric values must be finite, but was {}
- Expected a long but was {}{}
- Expected an int but was {}{}
- Nesting problem.
- Dangling name: {}
AI-assisted analysis of google/gson@310ac341f2 (2026-08-10).
Data as JSON: /api/errors/7f30351c40e03980.
Report an issue: GitHub.