google/gson · error · RuntimeException

Failed to invoke constructor '${ReflectionHelper.constructor

Error message

Failed to invoke constructor '${ReflectionHelper.constructorToString(constructor)}' with no args

What it means

Thrown when Constructor.newInstance() raises InstantiationException while invoking a located no-args constructor. The source comment states this should be impossible because abstract classes are filtered earlier, so it indicates a runtime inconsistency — typically R8/proguard altering the class after Gson's static checks, or exotic classloader/JVM behavior.

Source

Thrown at gson/src/main/java/com/google/gson/internal/ConstructorConstructor.java:268

    // Only try to make accessible if allowed; in all other cases checks above should
    // have verified that constructor is accessible
    if (filterResult == FilterResult.ALLOW) {
      String exceptionMessage = ReflectionHelper.tryMakeAccessible(constructor);
      if (exceptionMessage != null) {
        return new ThrowingObjectConstructor<>(exceptionMessage);
      }
    }

    return () -> {
      try {
        @SuppressWarnings("unchecked") // T is the same raw type as is requested
        T newInstance = (T) constructor.newInstance();
        return newInstance;
      }
      // Note: InstantiationException should be impossible because check at start of method made
      // sure that class is not abstract
      catch (InstantiationException e) {
        throw new RuntimeException(
            "Failed to invoke constructor '"
                + ReflectionHelper.constructorToString(constructor)
                + "' with no args",
            e);
      } catch (InvocationTargetException e) {
        // TODO: don't wrap if cause is unchecked?
        // TODO: JsonParseException ?
        throw new RuntimeException(
            "Failed to invoke constructor '"
                + ReflectionHelper.constructorToString(constructor)
                + "' with no args",
            e.getCause());
      } catch (IllegalAccessException e) {
        throw ReflectionHelper.createExceptionForUnexpectedIllegalAccess(e);
      }
    };
  }

View on GitHub (pinned to 310ac341f2)

Solutions

  1. Register an InstanceCreator or TypeAdapter for the type to bypass reflective construction
  2. Ensure R8/ProGuard keep rules preserve the no-args constructor: -keep class com.example.MyClass { <init>(); }
  3. Verify the class is concrete and retains its no-args constructor in the shipped artifact

Example fix

// before - R8 strips no-args ctor, class becomes abstract at runtime
// (no explicit keep rule)

// after - proguard-rules.pro
-keep class com.example.MyModel { <init>(); }
Defensive patterns

Strategy: try-catch

Validate before calling

// Verify the class is concrete and has a no-args constructor at setup time
Class<?> c = MyType.class;
if (Modifier.isAbstract(c.getModifiers())) throw new IllegalStateException(c + " is abstract");
try {
  c.getDeclaredConstructor();
} catch (NoSuchMethodException e) {
  throw new IllegalStateException(c + " needs a no-args constructor", e);
}

Try / catch

try {
  return gson.fromJson(json, MyType.class);
} catch (RuntimeException e) {
  if (e.getMessage() != null && e.getMessage().startsWith("Failed to invoke constructor")) {
    // class state changed at runtime; register InstanceCreator or fix keep rules
    throw new MyParseException("Constructor invocation failed (R8/JVM issue?)", e);
  }
  throw e;
}

Prevention

When it happens

Trigger: A no-args constructor is found and the class passed the non-abstract check, but newInstance() still throws InstantiationException — e.g. the class became abstract at runtime after shrinking, or the constructor was removed between discovery and invocation.

Common situations: Android R8/proguard stripping or modifying classes; hot-swapped or dynamically-generated classes changing between discovery and use; unusual JVM/classloader behavior.

Related errors


AI-assisted analysis of google/gson@310ac341f2 (2026-08-10). Data as JSON: /api/errors/3aec2295853ab9ef. Report an issue: GitHub.