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
- Pair every beginArray with endArray and every beginObject with endObject, including in error/early-return paths.
- Restructure serializers so begin and end are in the same method (try/finally).
- Add unit tests asserting the produced JSON round-trips through a JsonReader.
- 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
- Pair begin/end in the same method using try/finally.
- Unit-test serializers with round-trip parse/emit.
- Avoid conditional end* calls; structure code so they always run.
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
- Already wrote a name, expecting a value.
- Dangling name
- Please begin an object before writing a name.
- JsonWriter is closed.
- Nesting limit reached
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)