{"record":{"id":"5ed7cb2862bf2cf1","repo":"google/gson","slug":"abstract-classes-can-t-be-instantiated-adjust-the","errorCode":null,"errorMessage":"Abstract classes can't be instantiated! Adjust the R8 configuration or register an InstanceCreator or a TypeAdapter for this type. Class name: ${c.getName()}\nSee ${TroubleshootingGuide.createUrl(\"r8-abstract-class\")}","messagePattern":"Abstract classes can't be instantiated! Adjust the R8 configuration or register an InstanceCreator or a TypeAdapter for this type\\. Class name: (.+?)\nSee (.+?)","errorType":"exception","errorClass":"JsonIOException","httpStatus":null,"severity":"error","filePath":"gson/src/main/java/com/google/gson/internal/ConstructorConstructor.java","lineNumber":424,"sourceCode":"   * ObjectConstructor}, which would then choose another way of creating the object. And it supports\n   * types which are only serialized but not deserialized (compared to directly throwing an\n   * exception when the {@code ObjectConstructor} is requested), e.g. when the runtime type of an\n   * object is inaccessible, but the compile-time type is accessible.\n   */\n  private static final class ThrowingObjectConstructor<T> implements ObjectConstructor<T> {\n    private final String exceptionMessage;\n\n    ThrowingObjectConstructor(String exceptionMessage) {\n      this.exceptionMessage = exceptionMessage;\n    }\n\n    @Override\n    public T construct() {\n      // New exception is created every time to avoid keeping a reference to an exception with\n      // potentially long stack trace, causing a memory leak\n      // (which would happen if the exception was already created when the\n      // `ThrowingObjectConstructor` is created)\n      throw new JsonIOException(exceptionMessage);\n    }\n  }\n\n  private static final class InstanceCreatorConstructor<T> implements ObjectConstructor<T> {\n    private final InstanceCreator<T> instanceCreator;\n    private final Type type;\n\n    InstanceCreatorConstructor(InstanceCreator<T> instanceCreator, Type type) {\n      this.instanceCreator = instanceCreator;\n      this.type = type;\n    }\n\n    @Override\n    public T construct() {\n      return instanceCreator.createInstance(type);\n    }\n  }\n}","sourceCodeStart":406,"sourceCodeEnd":442,"githubUrl":"https://github.com/google/gson/blob/310ac341f2f92a454b229bf21f70d2d18b2b6db7/gson/src/main/java/com/google/gson/internal/ConstructorConstructor.java#L406-L442","documentation":"Thrown when Gson attempts to deserialize into an abstract class and no InstanceCreator or TypeAdapter is registered. The message specifically calls out R8, which on Android can strip a class's default constructor and mark it abstract during optimization. The message includes a troubleshooting guide URL.","triggerScenarios":"Calling gson.fromJson into an abstract class type without InstanceCreator/TypeAdapter; OR after R8/proguard optimization removed the no-args constructor and marked the class abstract, defeating the no-args-constructor lookup path.","commonSituations":"Android R8 shrinking removing constructors; deserializing into abstract base types; model classes obfuscated/shrunk without keep rules; migrating to R8 full mode.","solutions":["Add R8/ProGuard keep rules: -keep class com.example.MyClass { <init>(); }","Register an InstanceCreator returning a concrete subtype","Register a custom TypeAdapter for the abstract type","Deserialize into a concrete subclass instead"],"exampleFix":"// before - R8 strips ctor, class becomes abstract\n// (no keep rule)\n\n// after - proguard-rules.pro\n-keep class com.example.MyModel { <init>(); }","handlingStrategy":"validation","validationCode":"// Check if target is abstract before deserializing\nClass<?> c = (Class<?>) targetType;\nif (Modifier.isAbstract(c.getModifiers()) && !c.isArray()) {\n  throw new IllegalArgumentException(\n    \"Cannot deserialize into abstract class \" + c.getName() + \"; register InstanceCreator\");\n}","typeGuard":"static boolean isR8Safe(Class<?> c) {\n  return !Modifier.isAbstract(c.getModifiers())\n      && c.getDeclaredConstructors().length > 0;\n}","tryCatchPattern":"try {\n  return gson.fromJson(json, MyAbstract.class);\n} catch (JsonIOException e) {\n  if (e.getMessage().startsWith(\"Abstract classes can't be instantiated\")) {\n    // add R8 keep rule or register InstanceCreator returning a concrete subtype\n    throw new MyParseException(\"Abstract class needs InstanceCreator or R8 keep rule\", e);\n  }\n  throw e;\n}","preventionTips":["Add ProGuard/R8 keep rules for model classes: -keep class com.example.** { <init>(); }","Register InstanceCreators for abstract types returning concrete subtypes","Run deserialization tests on the shrunk/minified release build, not just debug","Deserialize into concrete subtypes instead of abstract bases where possible"],"tags":["abstract-class","r8","proguard","instantiation","android"],"backgroundTag":null,"analyzedSha":"310ac341f2f92a454b229bf21f70d2d18b2b6db7","analyzedAt":"2026-08-10T02:58:47.455Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}