google/gson · error · IllegalStateException
JsonReader is closed
Error message
JsonReader is closed
What it means
JsonTreeReader.peek throws IllegalStateException('JsonReader is closed') when the only item on the stack is the SENTINEL_CLOSED marker, i.e. close() was previously called on this JsonTreeReader. After close(), the reader replaces its stack with the sentinel and any further peek/next/hasNext call hits this guard.
Source
Thrown at gson/src/main/java/com/google/gson/internal/bind/JsonTreeReader.java:166
} else if (o instanceof JsonObject) {
return JsonToken.BEGIN_OBJECT;
} else if (o instanceof JsonArray) {
return JsonToken.BEGIN_ARRAY;
} else if (o instanceof JsonPrimitive) {
JsonPrimitive primitive = (JsonPrimitive) o;
if (primitive.isString()) {
return JsonToken.STRING;
} else if (primitive.isBoolean()) {
return JsonToken.BOOLEAN;
} else if (primitive.isNumber()) {
return JsonToken.NUMBER;
} else {
throw new AssertionError();
}
} else if (o instanceof JsonNull) {
return JsonToken.NULL;
} else if (o == SENTINEL_CLOSED) {
throw new IllegalStateException("JsonReader is closed");
} else {
throw new MalformedJsonException(
"Custom JsonElement subclass " + o.getClass().getName() + " is not supported");
}
}
private Object peekStack() {
return stack[stackSize - 1];
}
@CanIgnoreReturnValue
private Object popStack() {
Object result = stack[--stackSize];
stack[stackSize] = null;
return result;
}
private void expect(JsonToken expected) throws IOException {View on GitHub (pinned to 8b8628c656)
Solutions
- Do not use a JsonReader after calling close(); create a fresh JsonReader for each parse.
- Do not put JsonReader in try-with-resources if you intend to inspect it afterwards — close only when fully done.
- Audit custom TypeAdapters that accept a JsonReader to ensure they don't close it (Gson owns the lifecycle).
- If a reader must be reused, re-create it from the original JsonElement/string each time.
Example fix
// before JsonReader r = new JsonTreeReader(jsonElement); r.close(); r.peek(); // IllegalStateException: JsonReader is closed // after JsonReader r = new JsonTreeReader(jsonElement); JsonElement e = JsonParser.parseReader(r); // use, then discard r = new JsonTreeReader(jsonElement); // recreate if needed
Defensive patterns
Strategy: validation
Validate before calling
// Track open/closed state of readers you hand out
AtomicBoolean open = new AtomicBoolean(true);
JsonReader r = new JsonTreeReader(element);
// later, before use:
if (!open.get()) throw new IllegalStateException("Reader already closed");
r.peek(); Try / catch
try {
reader.peek();
} catch (IllegalStateException e) {
if ("JsonReader is closed".equals(e.getMessage())) {
// recreate the reader or surface a clear error
reader = new JsonTreeReader(originalElement);
} else throw e;
} Prevention
- Treat JsonReader as single-use; create a new one per parse.
- Do not put JsonReader in try-with-resources unless you discard it immediately after.
- Document ownership in helper methods that accept a JsonReader.
When it happens
Trigger: Calling any read operation on a JsonTreeReader (constructed from a JsonElement) after invoking close() on it. Common in try-with-resources blocks where the JsonReader is auto-closed but reused afterwards.
Common situations: Wrapping JsonParser.parseReader in try-with-resources and then trying to use the reader; utility methods that close a JsonReader and the caller re-uses it; thread-handoff where one side closes and another reads.
Related errors
- Expected {expected} but was {peek}{location}
- Expected STRING but was {token}{location}
- Expected NUMBER but was {token}{location}
- JSON forbids NaN and infinities: {result}
- JsonReader is closed
AI-assisted analysis of google/gson@8b8628c656 (2026-08-04).
Data as JSON: /data/errors/2a70302d64d6c465.json.
Report an issue: GitHub.