{"id":"579fd59316f837dc","repo":"google/gson","slug":"json-must-have-only-one-top-level-value","errorCode":null,"errorMessage":"JSON must have only one top-level value.","messagePattern":"JSON must have only one top-level value\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"gson/src/main/java/com/google/gson/stream/JsonWriter.java","lineNumber":810,"sourceCode":"    if (context == NONEMPTY_OBJECT) { // first in object\n      out.write(formattedComma);\n    } else if (context != EMPTY_OBJECT) { // not in an object!\n      throw new IllegalStateException(\"Nesting problem.\");\n    }\n    newline();\n    replaceTop(DANGLING_NAME);\n  }\n\n  /**\n   * Inserts any necessary separators and whitespace before a literal value, inline array, or inline\n   * object. Also adjusts the stack to expect either a closing bracket or another element.\n   */\n  @SuppressWarnings(\"fallthrough\")\n  private void beforeValue() throws IOException {\n    switch (peek()) {\n      case NONEMPTY_DOCUMENT:\n        if (strictness != Strictness.LENIENT) {\n          throw new IllegalStateException(\"JSON must have only one top-level value.\");\n        }\n      // fall-through\n      case EMPTY_DOCUMENT: // first in document\n        replaceTop(NONEMPTY_DOCUMENT);\n        break;\n\n      case EMPTY_ARRAY: // first in array\n        replaceTop(NONEMPTY_ARRAY);\n        newline();\n        break;\n\n      case NONEMPTY_ARRAY: // another in array\n        out.append(formattedComma);\n        newline();\n        break;\n\n      case DANGLING_NAME: // value for name\n        out.append(formattedColon);","sourceCodeStart":792,"sourceCodeEnd":828,"githubUrl":"https://github.com/google/gson/blob/8b8628c65699bc4421696183c62ae0c1b9b281dc/gson/src/main/java/com/google/gson/stream/JsonWriter.java#L792-L828","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","solutions":["Wrap multiple top-level values in an array: beginArray() ... values ... endArray().","Use a fresh JsonWriter per top-level value (typical for NDJSON where each line is its own document).","Call writer.setStrictness(Strictness.LENIENT) if you genuinely want concatenated top-level values in one stream.","Restructure so there is exactly one root value, e.g. an object containing the additional data as fields."],"exampleFix":"// before (default LEGACY_STRICT)\nwriter.beginObject().name(\"a\").value(1).endObject();\nwriter.beginObject().name(\"b\").value(2).endObject(); // throws\n\n// after: one writer per document (NDJSON)\nfor (Item i : items) {\n  try (JsonWriter w = jsonWriter(out)) {\n    writeItem(w, i);\n  }\n}","handlingStrategy":"validation","validationCode":"// One value per writer in strict mode; create a new writer per document\npublic void writeNdJson(List<String> docs, Writer sink) throws IOException {\n  for (String doc : docs) {\n    // each line is its own complete document -> its own writer\n    try (JsonWriter w = jsonWriter(new BufferedWriter(sink))) {\n      writeDoc(w, doc);\n      w.flush();\n    }\n    sink.write('\\n');\n  }\n}","typeGuard":"// True if the writer may legally accept another top-level value\npublic boolean canAcceptTopLevel(boolean firstValueWritten, Strictness s) {\n  return !firstValueWritten || s == Strictness.LENIENT;\n}","tryCatchPattern":"try {\n  writer.beginObject();\n} catch (IllegalStateException e) {\n  // 'JSON must have only one top-level value.' -> start a fresh writer\n  writer.close();\n  JsonWriter fresh = jsonWriter(out);\n  fresh.setStrictness(Strictness.LENIENT); // if concatenated docs are intended\n  fresh.beginObject();\n}","preventionTips":["Treat one JsonWriter as one JSON document; create a new writer per document for streaming.","If you need multiple concatenated top-level values, call setStrictness(Strictness.LENIENT) explicitly and document the contract.","Wrap logically grouped data in an enclosing array/object instead of emitting several roots.","Assert firstValueWritten state in your serializer to fail fast on accidental reuse."],"tags":["json","gson","serialization","strictness","structure"],"analyzedSha":"8b8628c65699bc4421696183c62ae0c1b9b281dc","analyzedAt":"2026-08-04T19:12:22.202Z","schemaVersion":2}