google/gson · error · IllegalStateException

JSON must have only one top-level value.

Error message

JSON must have only one top-level value.

What it means

Thrown by JsonWriter.beforeValue() when strictness is not LENIENT (default LEGACY_STRICT) and a value is written after a top-level value has already completed (peek() == NONEMPTY_DOCUMENT). RFC 8259 permits exactly one top-level JSON value, so a compliant writer rejects the second. LENIENT mode permits multiple concatenated top-level values (useful for JSON streaming/NDJSON-like protocols).

Source

Thrown at gson/src/main/java/com/google/gson/stream/JsonWriter.java:810

    if (context == NONEMPTY_OBJECT) { // first in object
      out.write(formattedComma);
    } else if (context != EMPTY_OBJECT) { // not in an object!
      throw new IllegalStateException("Nesting problem.");
    }
    newline();
    replaceTop(DANGLING_NAME);
  }

  /**
   * Inserts any necessary separators and whitespace before a literal value, inline array, or inline
   * object. Also adjusts the stack to expect either a closing bracket or another element.
   */
  @SuppressWarnings("fallthrough")
  private void beforeValue() throws IOException {
    switch (peek()) {
      case NONEMPTY_DOCUMENT:
        if (strictness != Strictness.LENIENT) {
          throw new IllegalStateException("JSON must have only one top-level value.");
        }
      // fall-through
      case EMPTY_DOCUMENT: // first in document
        replaceTop(NONEMPTY_DOCUMENT);
        break;

      case EMPTY_ARRAY: // first in array
        replaceTop(NONEMPTY_ARRAY);
        newline();
        break;

      case NONEMPTY_ARRAY: // another in array
        out.append(formattedComma);
        newline();
        break;

      case DANGLING_NAME: // value for name
        out.append(formattedColon);

View on GitHub (pinned to 8b8628c656)

Solutions

  1. Wrap multiple top-level values in an array: beginArray() ... values ... endArray().
  2. Use a fresh JsonWriter per top-level value (typical for NDJSON where each line is its own document).
  3. Call writer.setStrictness(Strictness.LENIENT) if you genuinely want concatenated top-level values in one stream.
  4. Restructure so there is exactly one root value, e.g. an object containing the additional data as fields.

Example fix

// before (default LEGACY_STRICT)
writer.beginObject().name("a").value(1).endObject();
writer.beginObject().name("b").value(2).endObject(); // throws

// after: one writer per document (NDJSON)
for (Item i : items) {
  try (JsonWriter w = jsonWriter(out)) {
    writeItem(w, i);
  }
}
Defensive patterns

Strategy: validation

Validate before calling

// One value per writer in strict mode; create a new writer per document
public void writeNdJson(List<String> docs, Writer sink) throws IOException {
  for (String doc : docs) {
    // each line is its own complete document -> its own writer
    try (JsonWriter w = jsonWriter(new BufferedWriter(sink))) {
      writeDoc(w, doc);
      w.flush();
    }
    sink.write('\n');
  }
}

Type guard

// True if the writer may legally accept another top-level value
public boolean canAcceptTopLevel(boolean firstValueWritten, Strictness s) {
  return !firstValueWritten || s == Strictness.LENIENT;
}

Try / catch

try {
  writer.beginObject();
} catch (IllegalStateException e) {
  // 'JSON must have only one top-level value.' -> start a fresh writer
  writer.close();
  JsonWriter fresh = jsonWriter(out);
  fresh.setStrictness(Strictness.LENIENT); // if concatenated docs are intended
  fresh.beginObject();
}

Prevention

When it happens

Trigger: Calling writer.value(...) / beginObject() / beginArray() a second time after the first top-level value was fully written and its container (if any) closed, while in LEGACY_STRICT or STRICT. Reusing a single JsonWriter to emit several independent JSON objects back-to-back; writing a value, then writing another without an enclosing array.

Common situations: Building NDJSON / JSON Lines over one writer (default mode forbids it); request-scoped JsonWriter reused for multiple payload fragments; serializers that write a status object then try to append a second; test harness writing multiple JSON values to one StringWriter.

Related errors


AI-assisted analysis of google/gson@8b8628c656 (2026-08-04). Data as JSON: /data/errors/579fd59316f837dc.json. Report an issue: GitHub.