{"record":{"id":"e185c2f284eebbb2","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/310ac341f2f92a454b229bf21f70d2d18b2b6db7/gson/src/main/java/com/google/gson/Gson.java#L1088-L1124","documentation":"Thrown by Gson.fromJson(JsonReader, TypeToken) right after a TypeAdapter.read returns: if the returned object is non-null but is not an instance of the requested (boxed) raw type, a ClassCastException is thrown naming the adapter, the requested type, and the actual class. This catches buggy/misregistered adapters that return the wrong type, failing fast instead of propagating a ClassCastException later at an opaque site.","triggerScenarios":"A custom TypeAdapter registered for type A whose read() returns an instance of B; registering an adapter for the generic-erased raw type while read() returns a subtype incompatible with the declared TypeToken; reusing one adapter for multiple types via unchecked casts; adapter returning a Map where a POJO is expected.","commonSituations":"Unchecked cast inside a custom adapter; adapter written for a different (but name-similar) type; raw-type registration losing generics (List vs List<Foo>); copy-paste of an adapter without updating its target type.","solutions":["Verify the adapter is registered for exactly the type its read() returns (use TypeToken, not raw Class, for generics).","Audit the custom TypeAdapter.read() to ensure it constructs/returns an instance assignable to T.","For generic types, register using a TypeToken captured in an anonymous subclass so generics are preserved.","Write a focused unit test asserting gson.fromJson(json, Type) returns an instanceof the expected type."],"exampleFix":"// before: adapter registered for Foo returns Bar\nclass FooAdapter extends TypeAdapter<Foo> {\n  public Foo read(JsonReader in) { return new Bar(); } // wrong type\n}\n\n// after\npublic Foo read(JsonReader in) { return new Foo(); }","handlingStrategy":"type-guard","validationCode":"// Unit-level guard: assert the adapter returns the right type\nObject result = adapter.read(new JsonReader(new StringReader(json)));\nif (!expectedRawType.isInstance(result)) {\n  throw new AssertionError(\"Adapter returned \" + result.getClass());\n}","typeGuard":"// Narrow the result of fromJson defensively\nstatic <T> Optional<T> tryAs(T value, Class<T> type) {\n  return type.isInstance(value) ? Optional.of(type.cast(value)) : Optional.empty();\n}","tryCatchPattern":"try { T v = gson.fromJson(reader, typeOfT); }\ncatch (ClassCastException e) {\n  if (e.getMessage().contains(\"returned wrong type\")) {\n    // the registered adapter is wrong; fix registration and retry\n  } else throw e;\n}","preventionTips":["Register adapters using TypeToken (not raw Class) for generic types.","Write a contract test: fromJson must return an instanceof the target type.","Avoid unchecked casts inside custom adapters."],"tags":["gson-core","type-adapter","classcast","type-safety"],"backgroundTag":null,"analyzedSha":"310ac341f2f92a454b229bf21f70d2d18b2b6db7","analyzedAt":"2026-08-10T02:58:47.455Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}