{"id":"4814083cb77b40cc","repo":"google/gson","slug":"expected-expected-but-was-peek-location","errorCode":null,"errorMessage":"Expected {expected} but was {peek}{location}","messagePattern":"Expected (.+?) but was (.+?)(.+?)","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/8b8628c65699bc4421696183c62ae0c1b9b281dc/gson/src/main/java/com/google/gson/internal/bind/JsonTreeReader.java#L168-L204","documentation":"JsonTreeReader.expect throws IllegalStateException('Expected ' + expected + ' but was ' + peek() + locationString) when the current token does not match the structural token the method requires. expect() is called by beginArray/endArray/beginObject/endObject/nextBoolean/nextNull, so any mismatch between what the caller asks for (e.g. beginArray) and what the tree actually has (e.g. an object) is rejected. The {expected} is the required JsonToken, {peek} the actual token, {location} the JSON path.","triggerScenarios":"Calling beginArray() when the current element is a JsonObject (or vice versa); endObject() when there is no open object; nextBoolean()/nextNull() when the token is not BOOLEAN/NULL. Typical of a custom TypeAdapter that assumes a layout that does not match the data.","commonSituations":"Custom adapter with hard-coded structure assumptions; data contract drift; wrong key ordering causing nextName misalignment; reusing a reader that's mid-structure.","solutions":["Inspect the {expected}/{peek}/{location} in the message to find where the layout assumption diverges from the data.","In custom adapters, branch on in.peek() rather than assuming a fixed token sequence.","Validate the JSON structure (object vs array at each path) before deserializing, or use lenient manual navigation.","Align the producer's data shape with the adapter, or update the adapter to match the new shape."],"exampleFix":"// before: adapter assumes array but data is object\nclass Adapter extends TypeAdapter<List<X>> {\n  public List<X> read(JsonReader in) throws IOException {\n    in.beginArray(); // IllegalStateException if JSON is an object\n    ...\n  }\n}\n\n// after: branch on token\npublic List<X> read(JsonReader in) throws IOException {\n  if (in.peek() == JsonToken.BEGIN_OBJECT) { /* read as map */ }\n  else { in.beginArray(); /* read as list */ }\n  ...\n}","handlingStrategy":"type-guard","validationCode":"// Check the next token matches what your adapter assumes before calling begin*/end*\nJsonToken t = reader.peek();\nif (t != JsonToken.BEGIN_OBJECT) {\n  throw new IllegalStateException(\"Expected object at \" + reader.getPath() + \", got \" + t);\n}\nreader.beginObject();","typeGuard":"static boolean isToken(JsonReader r, JsonToken expected) throws IOException {\n  return r.peek() == expected;\n}","tryCatchPattern":"try {\n  reader.beginArray();\n} catch (IllegalStateException e) {\n  if (e.getMessage() != null && e.getMessage().startsWith(\"Expected \")) {\n    throw new InvalidPayloadException(\"Structure mismatch at \" + reader.getPath(), e);\n  }\n  throw e;\n}","preventionTips":["Branch on peek() rather than assuming a fixed token layout.","Keep data contracts explicit and versioned.","Add round-trip tests for the JSON shape your adapters expect."],"tags":["jsonreader","json-tree","structure","type-adapter"],"analyzedSha":"8b8628c65699bc4421696183c62ae0c1b9b281dc","analyzedAt":"2026-08-04T19:12:22.202Z","schemaVersion":2}