{"id":"561455962f6f967f","repo":"google/gson","slug":"type-adapter-typeadapter-returned-wrong-type","errorCode":null,"errorMessage":"Type adapter '{typeAdapter}' returned wrong type; requested {typeOfT.getRawType()} but got instance of {object.getClass()}\nVerify that the adapter was registered for the correct type.","messagePattern":"Type adapter '(.+?)' returned wrong type; requested (.+?) but got instance of (.+?)\nVerify that the adapter was registered for the correct type\\.","errorType":"exception","errorClass":"ClassCastException","httpStatus":null,"severity":"error","filePath":"gson/src/main/java/com/google/gson/Gson.java","lineNumber":1106,"sourceCode":"      throws JsonIOException, JsonSyntaxException {\n    boolean isEmpty = true;\n    Strictness oldStrictness = reader.getStrictness();\n\n    if (this.strictness != null) {\n      reader.setStrictness(this.strictness);\n    } else if (reader.getStrictness() == Strictness.LEGACY_STRICT) {\n      // For backward compatibility change to LENIENT if reader has default strictness LEGACY_STRICT\n      reader.setStrictness(Strictness.LENIENT);\n    }\n\n    try {\n      JsonToken unused = reader.peek();\n      isEmpty = false;\n      TypeAdapter<T> typeAdapter = getAdapter(typeOfT);\n      T object = typeAdapter.read(reader);\n      Class<?> expectedTypeWrapped = Primitives.wrap(typeOfT.getRawType());\n      if (object != null && !expectedTypeWrapped.isInstance(object)) {\n        throw new ClassCastException(\n            \"Type adapter '\"\n                + typeAdapter\n                + \"' returned wrong type; requested \"\n                + typeOfT.getRawType()\n                + \" but got instance of \"\n                + object.getClass()\n                + \"\\nVerify that the adapter was registered for the correct type.\");\n      }\n      return object;\n    } catch (EOFException e) {\n      /*\n       * For compatibility with JSON 1.5 and earlier, we return null for empty\n       * documents instead of throwing.\n       */\n      if (isEmpty) {\n        return null;\n      }\n      throw new JsonSyntaxException(e);","sourceCodeStart":1088,"sourceCodeEnd":1124,"githubUrl":"https://github.com/google/gson/blob/8b8628c65699bc4421696183c62ae0c1b9b281dc/gson/src/main/java/com/google/gson/Gson.java#L1088-L1124","documentation":"Thrown by Gson.fromJson(JsonReader, TypeToken) after the type adapter's read() returns, when the returned object is non-null but is not an instance of the requested type (after boxing primitives). This means a custom TypeAdapter returned the wrong class, breaking the TypeAdapter<T> contract. It is a ClassCastException, surfaced directly (not wrapped) from fromJson.","triggerScenarios":"A custom TypeAdapter<Date> whose read() returns a String or Long; a TypeAdapter registered for type A but internally producing B; raw-type misuse where a TypeAdapter is registered with a raw class but produces a generic-substituted type; unsafe casts inside an adapter leaking an incompatible object.","commonSituations":"Adapters copied from examples targeting a different type than the one registered; generics erasure hiding a mismatch at compile time; an adapter that delegates to another adapter returning a broader type; @JsonAdapter pointing at an adapter written for a different class; refactoring a model without updating its adapter.","solutions":["Verify the TypeAdapter is registered for the exact type it produces (e.g. register it under Date.class only if read() returns a Date).","Make read() return the declared T; convert internally before returning.","If using generics, use TypeToken to capture the parameterized type and ensure the adapter's output matches.","Add a unit test asserting the returned object's class equals the requested type."],"exampleFix":"// before: adapter registered for Date returns a String\nclass BadDateAdapter extends TypeAdapter<Date> {\n  public Date read(JsonReader r) throws IOException { return (Date)(Object)r.nextString(); } // wrong\n}\n\n// after: convert to the declared return type\nclass GoodDateAdapter extends TypeAdapter<Date> {\n  public Date read(JsonReader r) throws IOException { return new Date(r.nextLong()); }\n  public void write(JsonWriter w, Date v) throws IOException { w.value(v.getTime()); }\n}","handlingStrategy":"type-guard","validationCode":"// After a custom adapter read, verify the returned type matches\nTypeAdapter<Date> adapter = ...;\nDate result = adapter.read(reader);\nif (result != null && !(result instanceof Date)) {\n  throw new IllegalStateException(\"Adapter returned \" + result.getClass());\n}","typeGuard":"static <T> boolean returnsCorrectType(TypeAdapter<T> adapter, JsonReader r, Class<T> expected)\n    throws IOException {\n  T v = adapter.read(r);\n  return v == null || expected.isInstance(v);\n}","tryCatchPattern":"try {\n  Foo f = gson.fromJson(json, TypeToken.get(Foo.class));\n} catch (ClassCastException e) {\n  if (e.getMessage().contains(\"returned wrong type\")) {\n    // an adapter is registered for the wrong type; fix the registration\n  } else throw e;\n}","preventionTips":["Register each TypeAdapter under the exact type its read() returns.","Add a unit test asserting the adapter's output class equals the requested type.","Avoid unchecked casts inside adapters; convert explicitly before returning.","When using @JsonAdapter, ensure the annotation targets the matching class."],"tags":["gson-core","type-adapter","classcast","deserialization","contract-violation"],"analyzedSha":"8b8628c65699bc4421696183c62ae0c1b9b281dc","analyzedAt":"2026-08-04T19:12:22.202Z","schemaVersion":2}