{"record":{"id":"5a03680c38e8b5cd","repo":"google/gson","slug":"unexpected-token-peeked-5a0368","errorCode":null,"errorMessage":"Unexpected token: ${peeked}","messagePattern":"Unexpected token: (.+?)","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"gson/src/main/java/com/google/gson/internal/bind/ObjectTypeAdapter.java","lineNumber":104,"sourceCode":"        return null;\n    }\n  }\n\n  /** Reads an {@code Object} which cannot have any nested elements */\n  private Object readTerminal(JsonReader in, JsonToken peeked) throws IOException {\n    switch (peeked) {\n      case STRING:\n        return in.nextString();\n      case NUMBER:\n        return toNumberStrategy.readNumber(in);\n      case BOOLEAN:\n        return in.nextBoolean();\n      case NULL:\n        in.nextNull();\n        return null;\n      default:\n        // When read(JsonReader) is called with JsonReader in invalid state\n        throw new IllegalStateException(\"Unexpected token: \" + peeked);\n    }\n  }\n\n  @Override\n  public Object read(JsonReader in) throws IOException {\n    // Either List or Map\n    Object current;\n    JsonToken peeked = in.peek();\n\n    current = tryBeginNesting(in, peeked);\n    if (current == null) {\n      return readTerminal(in, peeked);\n    }\n\n    Deque<Object> stack = new ArrayDeque<>();\n\n    while (true) {\n      while (in.hasNext()) {","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/google/gson/blob/310ac341f2f92a454b229bf21f70d2d18b2b6db7/gson/src/main/java/com/google/gson/internal/bind/ObjectTypeAdapter.java#L86-L122","documentation":"ObjectTypeAdapter.readTerminal() only accepts STRING, NUMBER, BOOLEAN, and NULL. For any other token (NAME, END_ARRAY, END_OBJECT, END_DOCUMENT) it throws IllegalStateException(\"Unexpected token: ...\"). The comment in source notes this happens 'when read(JsonReader) is called with JsonReader in invalid state' - typically a bug in a custom adapter that mis-advances the reader before delegating to Object deserialization, rather than malformed user JSON per se.","triggerScenarios":"A custom TypeAdapter that calls nextName()/beginObject() and then delegates the reader to Object deserialization without leaving it positioned at a value; calling fromJson where the reader is at a structural token rather than a value; misusing the reader API in streaming code.","commonSituations":"Custom adapter that consumes the wrong tokens; incorrect manual JsonReader usage; a TypeAdapter that partially reads an object then hands off to Gson's Object adapter.","solutions":["Ensure the JsonReader is positioned at a value token before delegating to Object deserialization","Do not call nextName()/beginObject() and then pass the reader to ObjectTypeAdapter without proper positioning","Validate JSON structure (or unit-test the adapter) to confirm the reader state at delegation points"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Before delegating to Object deserialization, ensure the reader is at a value token\nJsonToken t = reader.peek();\nif (t == JsonToken.NAME || t == JsonToken.END_ARRAY || t == JsonToken.END_OBJECT || t == JsonToken.END_DOCUMENT) {\n    throw new IllegalStateException(\"Reader not positioned at a value: \" + t);\n}\nObject o = gson.getAdapter(Object.class).read(reader);","typeGuard":null,"tryCatchPattern":"try {\n  Object o = gson.fromJson(reader, Object.class);\n} catch (IllegalStateException e) {\n  // 'Unexpected token: <token>': reader was at a structural token; reposition before retry\n}","preventionTips":["Always leave the JsonReader positioned at a value before delegating to Object deserialization","Do not interleave nextName()/beginObject() with Object adapter delegation","Unit-test custom adapters to confirm reader state at handoff points"],"tags":["json","deserialization","type-mismatch","object-model","reader-state"],"backgroundTag":null,"analyzedSha":"310ac341f2f92a454b229bf21f70d2d18b2b6db7","analyzedAt":"2026-08-10T02:58:47.455Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}