google/gson · error · IllegalStateException

Nesting problem.

Error message

Nesting problem.

What it means

Thrown by JsonWriter.closeScope() (JsonWriter.java:456-459) when the current stack context is neither the matching empty nor nonempty scope for the bracket being closed, e.g. calling endArray() while the top of stack is an object scope (or vice versa). The writer tracks scopes on a stack and refuses to emit a closing bracket that would produce malformed JSON.

Solutions

  1. Pair every beginArray with endArray and every beginObject with endObject, including in error/early-return paths.
  2. Restructure serializers so begin and end are in the same method (try/finally).
  3. Add unit tests asserting the produced JSON round-trips through a JsonReader.
  4. Use a stack/depth counter in your serializer to validate balance before writing.

Example fix

// before
writer.beginObject();
writer.name("a").value(1);
writer.endArray(); // wrong scope

// after
writer.beginObject();
writer.name("a").value(1);
writer.endObject();
Defensive patterns

Strategy: validation

Validate before calling

// Enforce balance in your serializer with try/finally.
writer.beginArray();
try {
  for (T item : items) writeItem(writer, item);
} finally {
  writer.endArray();
}

Type guard

// No public scope introspection on JsonWriter; track depth in your own serializer.
class BalancedWriter {
  private int depth;
  void beginArray() throws IOException { w.beginArray(); depth++; }
  void endArray() throws IOException { w.endArray(); depth--; }
}

Try / catch

try {
  writer.endArray();
} catch (IllegalStateException e) {
  // scope mismatch: log and recover the document state, or fail fast
  throw e;
}

Prevention

When it happens

Trigger: Calling endArray() when the writer is inside an object (top scope EMPTY_OBJECT/NONEMPTY_OBJECT), or endObject() inside an array. Also calling end* more times than begin*, or mismatching nesting in recursive serializers.

Common situations: Hand-written serializers with unbalanced begin/end; conditional branches that call beginArray but skip endArray on an early return; copy-paste errors swapping endObject/endArray; serializers that delegate to sub-writers and mis-track the scope.

Related errors


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

Appendix: source

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

  public JsonWriter endObject() throws IOException {
    return closeScope(EMPTY_OBJECT, NONEMPTY_OBJECT, '}');
  }

  /** Enters a new scope by appending any necessary whitespace and the given bracket. */
  @CanIgnoreReturnValue
  private JsonWriter openScope(int empty, char openBracket) throws IOException {
    beforeValue();
    push(empty);
    out.write(openBracket);
    return this;
  }

  /** Closes the current scope by appending any necessary whitespace and the given bracket. */
  @CanIgnoreReturnValue
  private JsonWriter closeScope(int empty, int nonempty, char closeBracket) throws IOException {
    int context = peek();
    if (context != nonempty && context != empty) {
      throw new IllegalStateException("Nesting problem.");
    }
    if (deferredName != null) {
      throw new IllegalStateException("Dangling name: " + deferredName);
    }

    stackSize--;
    if (context == nonempty) {
      newline();
    }
    out.write(closeBracket);
    return this;
  }

  private void push(int newTop) {
    if (stackSize == stack.length) {
      stack = Arrays.copyOf(stack, stackSize * 2);
    }
    stack[stackSize++] = newTop;

View on GitHub (pinned to 310ac341f2)