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
- 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.
- Guard with peek(): if the token is NAME/END_*/END_DOCUMENT, handle or skip rather than calling the Object adapter.
- If the input may be empty, check hasNext()/peek()==END_DOCUMENT before reading.
- 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
- Use peek() to confirm a value/container token before Object deserialization.
- Consume NAME/end tokens correctly in custom adapters that delegate to the Object adapter.
- Handle END_DOCUMENT for possibly-empty input.
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
- Unexpected {peeked} when reading a JsonElement.
- duplicate key: {key}
- Expecting number, got: " + jsonToken + "; at path " + in.get
- Failed parsing '" + s + "' as BigDecimal; at path " + in.get
- Failed parsing '" + s + "' as BigInteger; at path " + in.get
AI-assisted analysis of google/gson@8b8628c656 (2026-08-04).
Data as JSON: /data/errors/f343eef750889ce8.json.
Report an issue: GitHub.