google/gson · error · MalformedJsonException
${message}${locationString()} See ${TroubleshootingGuide.cre
Error message
${message}${locationString()}
See ${TroubleshootingGuide.createUrl("malformed-json")} What it means
This is the single construction site in JsonReader.syntaxError(String): it builds a MalformedJsonException (an IOException subclass) whose message is '<reason> at line L column C path $...' plus a link to the malformed-json troubleshooting page. Nearly every JSON grammar violation in JsonReader is routed through this method, so the visible message is the reason text (e.g. 'Unterminated string', 'Expected name', 'JSON forbids NaN and infinities', 'Use JsonReader.setStrictness(Strictness.LENIENT) to accept malformed JSON'). The location string pinpoints where in the input the parser failed.
Source
Thrown at gson/src/main/java/com/google/gson/stream/JsonReader.java:1840
if (strictness == Strictness.STRICT) {
throw syntaxError("Invalid escaped character \"'\" in strict mode");
}
case '"':
case '\\':
case '/':
return escaped;
default:
// throw error when none of the above cases are matched
throw syntaxError("Invalid escape sequence");
}
}
/**
* Throws a new {@link MalformedJsonException} with the given message and information about the
* current location.
*/
private MalformedJsonException syntaxError(String message) throws MalformedJsonException {
throw new MalformedJsonException(
message + locationString() + "\nSee " + TroubleshootingGuide.createUrl("malformed-json"));
}
private IllegalStateException unexpectedTokenError(String expected) throws IOException {
JsonToken peeked = peek();
String troubleshootingId =
peeked == JsonToken.NULL ? "adapter-not-null-safe" : "unexpected-json-structure";
return new IllegalStateException(
"Expected "
+ expected
+ " but was "
+ peek()
+ locationString()
+ "\nSee "
+ TroubleshootingGuide.createUrl(troubleshootingId));
}
/** Consumes the non-execute prefix if it exists. */View on GitHub (pinned to 8b8628c656)
Solutions
- Inspect the message reason text and location (line/column/path) to find the exact bad token, then fix the producer.
- If the input intentionally uses lenient syntax (comments, unquoted keys, NaN), call reader.setStrictness(Strictness.LENIENT) before reading.
- Validate/prettify the payload with a strict parser upstream, or run it through JsonReader in LENIENT then re-emit canonical JSON.
- For truncated streaming input, ensure the source writes complete JSON before the reader consumes it; for NDJSON, create a new JsonReader per line/document.
Example fix
// before
try (JsonReader reader = new JsonReader(new StringReader(payload))) {
reader.beginObject(); // throws MalformedJsonException on bad token
}
// after: tolerate lenient producer + handle parse failure
try (JsonReader reader = new JsonReader(new StringReader(payload))) {
reader.setStrictness(Strictness.LENIENT);
reader.beginObject();
} catch (MalformedJsonException e) {
log.warn("Unparseable JSON payload: {}", e.getMessage());
} Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-validate JSON with a strict parser before handing off, or set LENIENT for tolerant reads
public JsonReader lenientReader(String payload) {
JsonReader r = new JsonReader(new StringReader(payload));
r.setStrictness(Strictness.LENIENT); // accept comments, unquoted keys, NaN, etc.
return r;
} Type guard
// Cheap structural pre-check before full parse: balanced brackets and non-empty
public boolean looksLikeCompleteJson(String s) {
if (s == null || s.isBlank()) return false;
int depth = 0; boolean inStr = false; boolean esc = false;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (esc) { esc = false; continue; }
if (c == '\\') { esc = true; continue; }
if (c == '"') { inStr = !inStr; continue; }
if (inStr) continue;
if (c == '{' || c == '[') depth++;
else if (c == '}' || c == ']') depth--;
}
return !inStr && depth == 0;
} Try / catch
try (JsonReader reader = new JsonReader(source)) {
// ... parse ...
} catch (MalformedJsonException e) {
// message includes reason + 'at line L column C path $...' + troubleshooting URL
throw new MyParseException("Bad JSON at " + extractLocation(e.getMessage()), e);
} Prevention
- Set Strictness.LENIENT on JsonReader when consuming input that may use comments, unquoted keys, single quotes, or NaN literals.
- For NDJSON, create a fresh JsonReader per line/document rather than feeding the whole stream to one reader.
- Validate producer output with a strict parser in tests/CI so malformed payloads are caught before deployment.
- Use the line/column/path in the exception message to localize the defect, then fix the producer rather than silencing the error.
- Guard against truncated network responses by checking Content-Length / read completeness before parsing.
When it happens
Trigger: Any malformed input to JsonReader: unterminated array/object/string (lines 598, 614, 1192, 1281, 1595), expected name/colon/value (631, 640, 657, 694, 724), NaN/Infinity literal in non-LENIENT mode (1063), invalid escape sequence or malformed \u (1771, 1778, 1792, 1815, 1823, 1831), strict-mode unquoted strings/comments (checkLenient, 1637), non-ASCII in strict strings (1884). Also fires when nextDouble() encounters NaN/Infinity and strictness != LENIENT.
Common situations: Reading truncated network responses; parsing JSON produced by lenient writers that emitted NaN/Infinity, comments, single-quoted strings, or unquoted keys; feeding concatenated NDJSON to one JsonReader; corrupted files with stray BOM/control characters; version skew where a producer uses LENIENT features the consumer (default LEGACY_STRICT) rejects; BOM or trailing data after the root value.
Related errors
- String contains non-ASCII characters: {s}{location}
- Numeric values must be finite, but was " + string
- String created by " + numberClass + " is not a valid JSON nu
- JSON must have only one top-level value.
- Array must have size 1, but has size {size}
AI-assisted analysis of google/gson@8b8628c656 (2026-08-04).
Data as JSON: /data/errors/750de0e83fd58ce6.json.
Report an issue: GitHub.