google/gson · error · MalformedJsonException

See

Error message

${message}${locationString()}
See ${TroubleshootingGuide.createUrl("malformed-json")}

What it means

Thrown by JsonReader.syntaxError() as a com.google.gson.stream.MalformedJsonException (an IOException subclass). It is the single funnel for ~21 distinct syntax violations in the reader (unterminated array/object/string/escape/comment, 'Expected name', 'Expected :', 'Expected value', 'Unexpected value', 'JSON forbids NaN and infinities', 'Malformed Unicode escape', 'Invalid escape sequence', 'Cannot escape a newline character in strict mode', 'Invalid escaped character in strict mode', 'String contains non-ASCII characters'). The message is composed as the specific reason plus a location string ('at line L column C path $...') and a troubleshooting URL. The exception is raised at JsonReader.java:1840-1841.

Solutions

  1. Inspect the reported line/column/path in the message to locate the offending byte, then fix the source data at that position.
  2. Validate the input with a strict JSON parser or jsonlint before feeding it to Gson to get a clearer error.
  3. If the input uses lenient syntax (comments, single quotes, unquoted keys, multiple top-level values), set reader.setStrictness(Strictness.LENIENT) before reading.
  4. For truncated streams, ensure the producer writes complete JSON and the transport does not cut off mid-document; read fully into memory before parsing if partial reads are possible.
  5. If you cannot change the data, switch to a tolerant reader or pre-process (strip comments, normalize quotes) before parsing.

Example fix

// before
JsonReader r = new JsonReader(new StringReader("{'k': 1,}")); // single quotes + trailing comma
r.beginObject(); // throws MalformedJsonException: ... See ...#malformed-json

// after (accept lenient input)
JsonReader r = new JsonReader(new StringReader("{'k': 1,}"));
r.setStrictness(Strictness.LENIENT);
r.beginObject();
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-validate the payload with a strict parser before handing to Gson.
// Using javax.json (Jakarta JSON-P) as an independent validator:
import javax.json.Json;
import javax.json.JsonReader;
// returns normally if valid, throws on malformed input
void validateStrict(String payload) {
  try (JsonReader r = Json.createReader(new StringReader(payload))) {
    r.read(); // throws JsonParsingException on any syntax error
  }
}

Type guard

null

Try / catch

try {
  T obj = gson.fromJson(payload, type);
} catch (com.google.gson.JsonSyntaxException e) {
  // Gson wraps MalformedJsonException in JsonSyntaxException when using Gson.fromJson(...)
  Throwable cause = e.getCause();
  if (cause instanceof com.google.gson.stream.MalformedJsonException) {
    // message already contains line/column/path and a troubleshooting URL
    log.warn("Malformed JSON rejected: {}", cause.getMessage());
  }
  throw e;
}

// When using JsonReader directly:
try (JsonReader r = new JsonReader(reader)) {
  r.setStrictness(Strictness.STRICT);
  // ... consume ...
} catch (com.google.gson.stream.MalformedJsonException e) {
  // e.getMessage() ends with 'See https://github.com/google/gson/blob/main/Troubleshooting.md#malformed-json'
  throw new InvalidPayloadException("Bad JSON input: " + e.getMessage(), e);
}

Prevention

When it happens

Trigger: Calling any JsonReader consuming method (peek, nextString, nextBoolean, nextDouble, nextLong, nextName, skipValue, or Gson.fromJson via the reader) on input that violates RFC 8259; unterminated literals; stray characters between tokens; single quotes or unquoted names when strictness is STRICT/LEGACY_STRICT; a missing colon after a name; a trailing comma in strict mode; an embedded NaN/Infinity literal; a \uXXXX sequence with non-hex digits; a raw newline or apostrophe escaped only in STRICT mode.

Common situations: Reading JSON produced by a lenient writer or another language's serializer that emits comments, single-quoted strings, unquoted keys, or trailing commas; truncated network responses or partial file reads (unterminated structures); BOM or non-ASCII bytes in a stream decoded as the wrong charset; version mismatch where input previously tolerated under old lenient defaults now fails because Gson tightened defaults to LEGACY_STRICT; copy-pasting JSON with smart quotes or trailing punctuation.

Understand the failure class

Related errors


AI-assisted analysis of google/gson@310ac341f2 (2026-08-10). Data as JSON: /api/errors/750de0e83fd58ce6. Report an issue: GitHub.

Appendix: 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 310ac341f2)