google/gson · error · IllegalStateException

Unexpected token: " + peeked

Error message

Unexpected token: " + peeked

What it means

Thrown by ObjectTypeAdapter.readTerminal() as an IllegalStateException when the current token is not STRING, NUMBER, BOOLEAN, or NULL. ObjectTypeAdapter.read() only enters readTerminal when tryBeginNesting returned null (i.e. the token was not BEGIN_ARRAY/BEGIN_OBJECT), so reaching the default branch means the reader was on a NAME, END_ARRAY, END_OBJECT, or END_DOCUMENT token, indicating the reader is in an invalid state.

Source

Thrown at gson/src/main/java/com/google/gson/internal/bind/ObjectTypeAdapter.java:104

        return null;
    }
  }

  /** Reads an {@code Object} which cannot have any nested elements */
  private Object readTerminal(JsonReader in, JsonToken peeked) throws IOException {
    switch (peeked) {
      case STRING:
        return in.nextString();
      case NUMBER:
        return toNumberStrategy.readNumber(in);
      case BOOLEAN:
        return in.nextBoolean();
      case NULL:
        in.nextNull();
        return null;
      default:
        // When read(JsonReader) is called with JsonReader in invalid state
        throw new IllegalStateException("Unexpected token: " + peeked);
    }
  }

  @Override
  public Object read(JsonReader in) throws IOException {
    // Either List or Map
    Object current;
    JsonToken peeked = in.peek();

    current = tryBeginNesting(in, peeked);
    if (current == null) {
      return readTerminal(in, peeked);
    }

    Deque<Object> stack = new ArrayDeque<>();

    while (true) {
      while (in.hasNext()) {

View on GitHub (pinned to 8b8628c656)

Solutions

  1. Ensure the reader is positioned on a value token (STRING/NUMBER/BOOLEAN/NULL) or a container start (BEGIN_ARRAY/BEGIN_OBJECT) before calling read; consume NAME tokens with nextName() first.
  2. Guard with peek(): if the token is NAME/END_*/END_DOCUMENT, handle or skip rather than calling the Object adapter.
  3. If the input may be empty, check hasNext()/peek()==END_DOCUMENT before reading.
  4. Audit custom adapters that delegate to gson.getAdapter(Object.class).read(reader) to confirm cursor placement.

Example fix

// before: calling read() while reader is on NAME
JsonToken t = reader.peek(); // NAME
Object o = gson.fromJson(reader, Object.class); // throws Unexpected token: NAME

// after: consume name first, then read value
if (reader.peek() == JsonToken.NAME) reader.nextName();
Object o = gson.fromJson(reader, Object.class);
Defensive patterns

Strategy: validation

Validate before calling

// Validate reader is at a value/container token before reading Object
JsonToken t = reader.peek();
if (t == JsonToken.NAME || t == JsonToken.END_ARRAY
    || t == JsonToken.END_OBJECT || t == JsonToken.END_DOCUMENT) {
  throw new IllegalStateException("Reader not at a value: " + t);
}
Object o = gson.fromJson(reader, Object.class);

Type guard

static boolean atReadableToken(JsonReader r) throws IOException {
  JsonToken t = r.peek();
  return t == JsonToken.BEGIN_OBJECT || t == JsonToken.BEGIN_ARRAY
      || t == JsonToken.STRING || t == JsonToken.NUMBER
      || t == JsonToken.BOOLEAN || t == JsonToken.NULL;
}

Try / catch

try {
  return gson.fromJson(reader, Object.class);
} catch (IllegalStateException e) {
  if (e.getMessage() != null && e.getMessage().startsWith("Unexpected token:")) {
    reader.skipValue(); // resync and continue
    return null;
  }
  throw e;
}

Prevention

When it happens

Trigger: Deserializing into Object.class (or a field typed Object) when the JsonReader is positioned on a structural/end token rather than a value. Most often this is an internal-state error: read() was called at the wrong point, e.g. inside an object after nextName() was skipped, or after the stream was exhausted. It can also surface with custom readers that misuse the reader.

Common situations: Custom JsonReader usage that calls ObjectTypeAdapter.read() at the wrong cursor position; malformed driving of a partially-consumed reader; edge cases with lenient empty input (END_DOCUMENT); bugs in adapters that delegate to the Object adapter after consuming a NAME incorrectly.

Related errors


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