{"record":{"id":"ce370c6748abe1e4","repo":"google/gson","slug":"expected-expected-but-was-peek-at-path-pat","errorCode":null,"errorMessage":"Expected ${expected} but was ${peek} at path ${path}","messagePattern":"Expected (.+?) but was (.+?) at path (.+?)","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"gson/src/main/java/com/google/gson/internal/bind/JsonTreeReader.java","lineNumber":186,"sourceCode":"      throw new MalformedJsonException(\n          \"Custom JsonElement subclass \" + o.getClass().getName() + \" is not supported\");\n    }\n  }\n\n  private Object peekStack() {\n    return stack[stackSize - 1];\n  }\n\n  @CanIgnoreReturnValue\n  private Object popStack() {\n    Object result = stack[--stackSize];\n    stack[stackSize] = null;\n    return result;\n  }\n\n  private void expect(JsonToken expected) throws IOException {\n    if (peek() != expected) {\n      throw new IllegalStateException(\n          \"Expected \" + expected + \" but was \" + peek() + locationString());\n    }\n  }\n\n  private String nextName(boolean skipName) throws IOException {\n    expect(JsonToken.NAME);\n    Iterator<?> i = (Iterator<?>) peekStack();\n    Map.Entry<?, ?> entry = (Map.Entry<?, ?>) i.next();\n    String result = (String) entry.getKey();\n    pathNames[stackSize - 1] = skipName ? \"<skipped>\" : result;\n    push(entry.getValue());\n    return result;\n  }\n\n  @Override\n  public String nextName() throws IOException {\n    return nextName(false);\n  }","sourceCodeStart":168,"sourceCodeEnd":204,"githubUrl":"https://github.com/google/gson/blob/310ac341f2f92a454b229bf21f70d2d18b2b6db7/gson/src/main/java/com/google/gson/internal/bind/JsonTreeReader.java#L168-L204","documentation":"JsonTreeReader.expect compares the next token to an expected value (used by beginArray/endArray/beginObject/endObject/nextBoolean/nextNull). A mismatch throws IllegalStateException listing expected vs. actual token and the JSON path. This is the structural 'you said array but it's an object' failure for tree-backed parsing.","triggerScenarios":"Deserializing a type whose adapter calls beginArray() when the JSON object actually starts with '{', or endObject() when not at END_OBJECT. Common when a field type changes between producer and consumer (array vs object).","commonSituations":"Schema drift where a singleton object becomes an array (or vice-versa); polymorphic payloads; wrong target type passed to fromJson; element fed to JsonTreeReader that mismatches the expected shape.","solutions":["Align the target type with the actual JSON structure (List vs single object).","Register a custom TypeAdapter that tolerates both shapes (singleton-or-array pattern).","Inspect the reported path and token to localize the mismatch.","Add schema validation or contract tests between producer and consumer."],"exampleFix":"// before: JSON is {\"items\":{...}}\nGson().fromJson(json, Array<Items>::class.java) // Expected BEGIN_ARRAY but was BEGIN_OBJECT\n\n// after\ndata class Wrapper(val items: Items)\nGson().fromJson(json, Wrapper::class.java)","handlingStrategy":"validation","validationCode":"// Peek at structure before binding to a type\nJsonElement tree = JsonParser.parseString(json);\nif (tree.isJsonObject()) gson.fromJson(tree, Single.class);\nelse if (tree.isJsonArray()) gson.fromJson(tree, ListOf.class);\n// or accept both via a tolerant adapter","typeGuard":"static boolean isArrayShape(JsonElement e) { return e.isJsonArray(); }\nstatic boolean isObjectShape(JsonElement e) { return e.isJsonObject(); }","tryCatchPattern":"try {\n  gson.fromJson(reader, List.class);\n} catch (IllegalStateException e) {\n  if (e.getMessage().startsWith(\"Expected \") && e.getMessage().contains(\" but was \")) {\n    // structural mismatch; inspect token and retry with correct type\n    log.warn(\"Shape mismatch: {}\", e.getMessage());\n  }\n  throw e;\n}","preventionTips":["Keep producer and consumer schemas in sync.","Handle singleton-or-array patterns with a tolerant custom adapter.","Peek at the JSON shape before choosing the target type.","Add contract tests asserting array-vs-object shapes."],"tags":["json-tree-reader","structural-mismatch","deserialization","token"],"backgroundTag":null,"analyzedSha":"310ac341f2f92a454b229bf21f70d2d18b2b6db7","analyzedAt":"2026-08-10T02:58:47.455Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}