{"id":"f343eef750889ce8","repo":"google/gson","slug":"unexpected-token-peeked-f343ee","errorCode":null,"errorMessage":"Unexpected token: \" + peeked","messagePattern":"Unexpected token: \" \\+ peeked","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/8b8628c65699bc4421696183c62ae0c1b9b281dc/gson/src/main/java/com/google/gson/internal/bind/ObjectTypeAdapter.java#L86-L122","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: calling read() while reader is on NAME\nJsonToken t = reader.peek(); // NAME\nObject o = gson.fromJson(reader, Object.class); // throws Unexpected token: NAME\n\n// after: consume name first, then read value\nif (reader.peek() == JsonToken.NAME) reader.nextName();\nObject o = gson.fromJson(reader, Object.class);","handlingStrategy":"validation","validationCode":"// Validate reader is at a value/container token before reading Object\nJsonToken t = reader.peek();\nif (t == JsonToken.NAME || t == JsonToken.END_ARRAY\n    || t == JsonToken.END_OBJECT || t == JsonToken.END_DOCUMENT) {\n  throw new IllegalStateException(\"Reader not at a value: \" + t);\n}\nObject o = gson.fromJson(reader, Object.class);","typeGuard":"static boolean atReadableToken(JsonReader r) throws IOException {\n  JsonToken t = r.peek();\n  return t == JsonToken.BEGIN_OBJECT || t == JsonToken.BEGIN_ARRAY\n      || t == JsonToken.STRING || t == JsonToken.NUMBER\n      || t == JsonToken.BOOLEAN || t == JsonToken.NULL;\n}","tryCatchPattern":"try {\n  return gson.fromJson(reader, Object.class);\n} catch (IllegalStateException e) {\n  if (e.getMessage() != null && e.getMessage().startsWith(\"Unexpected token:\")) {\n    reader.skipValue(); // resync and continue\n    return null;\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["json","gson","deserialization","reader-state","object-adapter"],"analyzedSha":"8b8628c65699bc4421696183c62ae0c1b9b281dc","analyzedAt":"2026-08-04T19:12:22.202Z","schemaVersion":2}