{"id":"7938f819cc88dffd","repo":"google/gson","slug":"unexpected-token-peeked","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/JsonElementTypeAdapter.java","lineNumber":72,"sourceCode":"    }\n  }\n\n  /** Reads a {@link JsonElement} which cannot have any nested elements */\n  private JsonElement readTerminal(JsonReader in, JsonToken peeked) throws IOException {\n    switch (peeked) {\n      case STRING:\n        return new JsonPrimitive(in.nextString());\n      case NUMBER:\n        String number = in.nextString();\n        return new JsonPrimitive(new LazilyParsedNumber(number));\n      case BOOLEAN:\n        return new JsonPrimitive(in.nextBoolean());\n      case NULL:\n        in.nextNull();\n        return JsonNull.INSTANCE;\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 JsonElement read(JsonReader in) throws IOException {\n    // Optimization if value already exists as JsonElement\n    if (in instanceof JsonTreeReader) {\n      return ((JsonTreeReader) in).nextJsonElement();\n    }\n\n    // Either JsonArray or JsonObject\n    JsonElement current;\n    JsonToken peeked = in.peek();\n\n    current = tryBeginNesting(in, peeked);\n    if (current == null) {\n      return readTerminal(in, peeked);\n    }","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/google/gson/blob/8b8628c65699bc4421696183c62ae0c1b9b281dc/gson/src/main/java/com/google/gson/internal/bind/JsonElementTypeAdapter.java#L54-L90","documentation":"JsonElementTypeAdapter.readTerminal throws IllegalStateException('Unexpected token: ' + peeked) when the reader is positioned at a structural token (BEGIN_ARRAY, BEGIN_OBJECT, END_*, NAME, END_DOCUMENT) at a point where only a terminal value (STRING, NUMBER, BOOLEAN, NULL) is expected. This signals the JsonReader is in an invalid state for reading a single JsonElement value, typically because the caller advanced the reader incorrectly before delegating to JsonElementTypeAdapter. The {peeked} is the offending JsonToken.","triggerScenarios":"Calling JsonParser.parseReader(JsonReader) (or reading a JsonElement) after manually consuming part of the stream so the cursor sits on a NAME or END token; reusing a JsonReader past its logical end; mixing manual nextName()/beginObject() with JsonElement parsing on the same reader.","commonSituations":"Custom TypeAdapter that does some manual token reading then calls gson.getAdapter(JsonElement.class).read(in) at the wrong position; streaming parser reuse; tests that hand a JsonReader to multiple consumers.","solutions":["Ensure the JsonReader is positioned at the start of a value (STRING/NUMBER/BOOLEAN/NULL/BEGIN_ARRAY/BEGIN_OBJECT) before reading a JsonElement.","Do not interleave manual beginObject()/nextName() with JsonElement reads on the same reader — let one consumer own the cursor.","Create a fresh JsonReader for each parse, or rewind/recreate the source if you must re-read.","In a custom adapter, call in.peek() and handle structural tokens explicitly instead of delegating blindly."],"exampleFix":"// before\nJsonReader r = new JsonReader(new StringReader(\"{\\\"a\\\":1}\"));\nr.beginObject(); r.nextName();\nJsonElement e = JsonParser.parseReader(r); // positioned at NUMBER inside object -> IllegalStateException\n\n// after: parse the whole document, then navigate the tree\nJsonElement e = JsonParser.parseString(\"{\\\"a\\\":1}\");\nint a = e.getAsJsonObject().get(\"a\").getAsInt();","handlingStrategy":"validation","validationCode":"// Ensure the reader is positioned at a value token before reading a JsonElement\nJsonToken t = reader.peek();\nif (t == JsonToken.NAME || t == JsonToken.END_ARRAY\n    || t == JsonToken.END_OBJECT || t == JsonToken.END_DOCUMENT\n    || t == JsonToken.BEGIN_ARRAY || t == JsonToken.BEGIN_OBJECT) {\n  // BEGIN_ARRAY/BEGIN_OBJECT are fine for JsonElement; NAME/END_*/END_DOCUMENT are not\n}\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}","typeGuard":null,"tryCatchPattern":"try {\n  return JsonParser.parseReader(reader);\n} catch (IllegalStateException e) {\n  if (e.getMessage() != null && e.getMessage().startsWith(\"Unexpected token: \")) {\n    throw new IllegalStateException(\"Reader misaligned before JsonElement read at \" + reader.getPath(), e);\n  }\n  throw e;\n}","preventionTips":["Do not interleave manual token reading with JsonElement parsing on one reader.","Prefer parsing the whole document into a tree, then navigating.","Create a fresh JsonReader per parse operation."],"tags":["jsonelement","json-parsing","reader-state","streaming"],"analyzedSha":"8b8628c65699bc4421696183c62ae0c1b9b281dc","analyzedAt":"2026-08-04T19:12:22.202Z","schemaVersion":2}