{"record":{"id":"4c0fe24489f03430","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/310ac341f2f92a454b229bf21f70d2d18b2b6db7/gson/src/main/java/com/google/gson/internal/bind/JsonElementTypeAdapter.java#L54-L90","documentation":"JsonElementTypeAdapter.readTerminal only handles STRING, NUMBER, BOOLEAN, NULL. Reaching the default branch means the JsonReader reported a structural token (BEGIN_ARRAY/BEGIN_OBJECT/END_*) where a terminal value was expected, indicating the reader is in an inconsistent/corrupt state. This is normally only reachable through internal misuse or a buggy custom JsonReader.","triggerScenarios":"Calling JsonElementTypeAdapter.ADAPTER.read on a JsonReader whose peek() returns a structural token after the adapter already decided it is a terminal (e.g. concurrent mutation of the reader, or a custom JsonReader implementation returning inconsistent tokens).","commonSituations":"Sharing a JsonReader across threads; subclassing JsonReader incorrectly; calling internal adapter methods directly instead of via Gson.","solutions":["Do not share a single JsonReader between threads; create a fresh reader per parse.","Avoid subclassing JsonReader with inconsistent peek/next semantics.","Use Gson.fromJson rather than invoking JsonElementTypeAdapter directly.","File a Gson bug if the reader is a standard JsonReader and the failure is reproducible."],"exampleFix":"// before: shared reader mutated concurrently\nval reader = JsonReader(StringReader(json))\nthread { JsonElementTypeAdapter.ADAPTER.read(reader) }\nJsonElementTypeAdapter.ADAPTER.read(reader) // IllegalStateException\n\n// after: one reader per parse, single-threaded\nval reader = JsonReader(StringReader(json))\nval el = JsonElementTypeAdapter.ADAPTER.read(reader)","handlingStrategy":"try-catch","validationCode":"// Do not invoke JsonElementTypeAdapter directly; use Gson\n// If you must, ensure the reader is single-threaded and at a valid token\nJsonToken t = reader.peek();\nif (t == BEGIN_ARRAY || t == BEGIN_OBJECT || t == STRING || t == NUMBER || t == BOOLEAN || t == NULL) {\n  JsonElement e = JsonElementTypeAdapter.ADAPTER.read(reader);\n}","typeGuard":null,"tryCatchPattern":"try {\n  JsonElement e = JsonElementTypeAdapter.ADAPTER.read(reader);\n} catch (IllegalStateException ex) {\n  if (ex.getMessage().startsWith(\"Unexpected token\")) {\n    // reader in inconsistent state; reset and re-parse\n    throw new CorruptedReaderException(ex);\n  }\n  throw ex;\n}","preventionTips":["Never share a JsonReader across threads.","Prefer gson.fromJson over direct adapter calls.","Do not subclass JsonReader with inconsistent semantics.","Consume each reader exactly once."],"tags":["json-element","internal","reader-state","concurrency"],"backgroundTag":null,"analyzedSha":"310ac341f2f92a454b229bf21f70d2d18b2b6db7","analyzedAt":"2026-08-10T02:58:47.455Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}