google/gson · error · IllegalStateException

JsonReader is closed

Error message

JsonReader is closed

What it means

Thrown by JsonReader.doPeek (and any operation that triggers it) once the reader's scope stack top is JsonScope.CLOSED, i.e. after close() has been called. The reader is single-use; continuing to read from it is a programmer error, surfaced as IllegalStateException rather than silently returning stale data.

Source

Thrown at gson/src/main/java/com/google/gson/stream/JsonReader.java:674

        default:
          throw syntaxError("Expected ':'");
      }
    } else if (peekStack == JsonScope.EMPTY_DOCUMENT) {
      if (strictness == Strictness.LENIENT) {
        consumeNonExecutePrefix();
      }
      stack[stackSize - 1] = JsonScope.NONEMPTY_DOCUMENT;
    } else if (peekStack == JsonScope.NONEMPTY_DOCUMENT) {
      int c = nextNonWhitespace(false);
      if (c == -1) {
        peeked = PEEKED_EOF;
        return peeked;
      } else {
        checkLenient();
        pos--;
      }
    } else if (peekStack == JsonScope.CLOSED) {
      throw new IllegalStateException("JsonReader is closed");
    }

    int c = nextNonWhitespace(true);
    switch (c) {
      case ']':
        if (peekStack == JsonScope.EMPTY_ARRAY) {
          peeked = PEEKED_END_ARRAY;
          return peeked;
        }
      // fall-through to handle ",]"
      case ';':
      case ',':
        // In lenient mode, a 0-length literal in an array means 'null'.
        if (peekStack == JsonScope.EMPTY_ARRAY || peekStack == JsonScope.NONEMPTY_ARRAY) {
          checkLenient();
          pos--;
          peeked = PEEKED_NULL;
          return peeked;

View on GitHub (pinned to 8b8628c656)

Solutions

  1. Do not use a JsonReader after close(); perform all reads inside the same scope before closing.
  2. Avoid try-with-resources if the reader must outlive the current method; manage its lifecycle explicitly and close exactly once.
  3. If re-parsing is needed, create a fresh JsonReader from the original source.
  4. Guard with a boolean isOpen flag in your wrapper to fail with a clearer message.

Example fix

// before
try (JsonReader reader = new JsonReader(new StringReader(json))) {
  // ...
}
reader.peek(); // throws 'JsonReader is closed'

// after
JsonReader reader = new JsonReader(new StringReader(json));
try {
  // ... all reads here
} finally {
  reader.close();
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Track closed state in a wrapper
JsonReader reader = ...;
boolean closed = false;
// single source of close():
void closeReader() throws IOException { reader.close(); closed = true; }
// guard reads:
void safePeek() throws IOException {
  if (closed) throw new IllegalStateException("reader already closed");
  reader.peek();
}

Try / catch

try {
  reader.peek();
} catch (IllegalStateException e) {
  if (e.getMessage().equals("JsonReader is closed")) {
    // recreate reader or report upstream; do not silently ignore
  } else throw e;
}

Prevention

When it happens

Trigger: Calling any of peek(), hasNext(), beginObject(), nextString(), etc. on a JsonReader instance after close() has been invoked (e.g. via try-with-resources that auto-closes, then a later read attempt).

Common situations: Storing the JsonReader in a field and closing it in one method while another method still reads; returning a reader from a try-with-resources block; wrapping parsing logic where the cleanup path closes early on an exception but processing continues.

Related errors


AI-assisted analysis of google/gson@8b8628c656 (2026-08-04). Data as JSON: /data/errors/b066dcbdcf8b1544.json. Report an issue: GitHub.