{"id":"a228fe1914c7db9c","repo":"google/gson","slug":"nesting-problem","errorCode":null,"errorMessage":"Nesting problem.","messagePattern":"Nesting problem\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"gson/src/main/java/com/google/gson/stream/JsonWriter.java","lineNumber":458,"sourceCode":"  public JsonWriter endObject() throws IOException {\n    return closeScope(EMPTY_OBJECT, NONEMPTY_OBJECT, '}');\n  }\n\n  /** Enters a new scope by appending any necessary whitespace and the given bracket. */\n  @CanIgnoreReturnValue\n  private JsonWriter openScope(int empty, char openBracket) throws IOException {\n    beforeValue();\n    push(empty);\n    out.write(openBracket);\n    return this;\n  }\n\n  /** Closes the current scope by appending any necessary whitespace and the given bracket. */\n  @CanIgnoreReturnValue\n  private JsonWriter closeScope(int empty, int nonempty, char closeBracket) throws IOException {\n    int context = peek();\n    if (context != nonempty && context != empty) {\n      throw new IllegalStateException(\"Nesting problem.\");\n    }\n    if (deferredName != null) {\n      throw new IllegalStateException(\"Dangling name: \" + deferredName);\n    }\n\n    stackSize--;\n    if (context == nonempty) {\n      newline();\n    }\n    out.write(closeBracket);\n    return this;\n  }\n\n  private void push(int newTop) {\n    if (stackSize == stack.length) {\n      stack = Arrays.copyOf(stack, stackSize * 2);\n    }\n    stack[stackSize++] = newTop;","sourceCodeStart":440,"sourceCodeEnd":476,"githubUrl":"https://github.com/google/gson/blob/8b8628c65699bc4421696183c62ae0c1b9b281dc/gson/src/main/java/com/google/gson/stream/JsonWriter.java#L440-L476","documentation":"Thrown by JsonWriter.closeScope() (used by endArray/endObject) when the current scope on the stack is neither the matching empty nor nonempty scope. This indicates mismatched begin/end calls, e.g. calling endArray() when an object is currently open, or calling endObject() twice.","triggerScenarios":"Calling endArray() after beginObject() (or vice versa), or calling an end method without a corresponding begin, or emitting more end calls than begins. The scope mismatch is detected at JsonWriter.java:457.","commonSituations":"Branching serialization logic where one path emits an extra end or skips one; manual writer use with conditionals that forget to balance brackets; refactoring tree-building code and losing a begin/end pairing.","solutions":["Audit every beginObject/beginArray to ensure a matching endObject/endArray on every code path (use try-finally).","Build the structure first in memory and serialize, or use Gson's tree model, to avoid manual balancing.","Add unit tests that round-trip the writer output through a JsonReader to catch imbalance.","Use a state-tracking wrapper that asserts begin/end symmetry."],"exampleFix":"// before\nwriter.beginObject();\nif (cond) {\n  writer.endArray(); // wrong: should be endObject(), or no end at all\n}\n\n// after\nwriter.beginObject();\ntry {\n  // ... write fields\n} finally {\n  writer.endObject();\n}","handlingStrategy":"validation","validationCode":"// Use a wrapper that balances begin/end on every path\nJsonWriter w = ...;\nw.beginObject();\ntry {\n  // write fields\n} finally {\n  w.endObject();\n}\n// or assert symmetry in tests by round-tripping the produced JSON","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pair every beginObject()/beginArray() with a matching end in a try-finally.","Use Gson's POJO serialization or tree model to avoid manual scope balancing.","Add round-trip tests (write then read back) to catch imbalance.","Avoid mixing beginObject/beginArray within the same conditional branch."],"tags":["gson","jsonwriter","nesting","state-machine","api-misuse"],"analyzedSha":"8b8628c65699bc4421696183c62ae0c1b9b281dc","analyzedAt":"2026-08-04T19:12:22.202Z","schemaVersion":2}