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() (IllegalStateException) at JsonWriter.java:810 when a value is about to be written and the top-of-stack scope is already NONEMPTY_DOCUMENT, meaning a complete top-level JSON value was already produced. In Strictness.STRICT and LEGACY_STRICT (the default) RFC 8259 forbids more than one top-level value, so a second value/literal/array/object is rejected. Only Strictness.LENIENT falls through to allow concatenated top-level values.
Solutions
- Wrap multiple top-level values in a single array: beginArray() ... values ... endArray().
- If you genuinely need concatenated top-level values (NDJSON), call writer.setStrictness(Strictness.LENIENT) before writing.
- Use a fresh JsonWriter per top-level value instead of reusing one writer across values.
- Restructure the loop so each iteration produces exactly one complete document and flushes/closes before the next.
Example fix
// before
JsonWriter w = new JsonWriter(out); // default LEGACY_STRICT
w.value("first");
w.value("second"); // throws 'JSON must have only one top-level value.'
// after (option A: wrap in array)
JsonWriter w = new JsonWriter(out);
w.beginArray();
w.value("first");
w.value("second");
w.endArray();
// after (option B: lenient for NDJSON)
JsonWriter w = new JsonWriter(out);
w.setStrictness(Strictness.LENIENT);
w.value("first");
w.value("second"); Defensive patterns
Strategy: validation
Validate before calling
// Decide up front: multiple top-level values need an array OR lenient mode.
JsonWriter w = new JsonWriter(out);
boolean multipleTopLevel = values.size() > 1;
if (multipleTopLevel) {
// choose ONE strategy:
// w.setStrictness(Strictness.LENIENT); // for NDJSON
w.beginArray(); // or wrap
} Type guard
null
Try / catch
try {
writer.value(first);
writer.value(second);
} catch (IllegalStateException e) {
if (e.getMessage() != null && e.getMessage().contains("only one top-level value")) {
// cannot recover mid-stream safely; this is a design error
throw new IllegalStateException("Wrap values in an array or use Strictness.LENIENT", e);
}
throw e;
} Prevention
- Default strictness is LEGACY_STRICT: design for exactly one top-level value per writer.
- For record streams use Strictness.LENIENT explicitly, or a fresh writer per record.
- Document the chosen policy at the writer construction site.
- Add a test that writes N>1 values to catch regressions when strictness changes.
When it happens
Trigger: Writing two literals (e.g. value("a"); value("b");) at document top level with default strictness; calling beginArray() after a complete top-level object; reusing a single JsonWriter to emit multiple independent JSON values without wrapping them in an array; loops that write multiple values forgetting to open a container first.
Common situations: Streaming multiple records to one writer (log/event streams) where each record is its own JSON object on the same writer; migrating from lenient to default strictness and suddenly hitting the limit; NDJSON-style emission attempted through a single default JsonWriter.
Related errors
- Incomplete document
- See
- Already wrote a name, expecting a value.
- Dangling name
- Did not consume the entire document.
AI-assisted analysis of google/gson@310ac341f2 (2026-08-10).
Data as JSON: /api/errors/579fd59316f837dc.
Report an issue: GitHub.
Appendix: 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 310ac341f2)