google/gson · error · RuntimeException
Failed to invoke constructor
Error message
Failed to invoke constructor '${constructor}' with args ${accumulator} What it means
After reading all JSON values for a record, Gson invokes the canonical constructor via Constructor.newInstance. This specific error catches InstantiationException or IllegalArgumentException, meaning the arguments passed to the constructor were of the wrong type or the class could not be instantiated — typically caused by a custom TypeAdapter returning an object whose runtime type does not match the component's declared type.
Solutions
- Inspect the caused-by IllegalArgumentException for the specific type mismatch
- Verify every custom TypeAdapter registered for component types returns the correct runtime type
- Temporarily remove custom adapters to isolate which one produces the wrong type
Example fix
// before — adapter returns Integer where Long is expected
record Measurement(long value) {}
gsonBuilder.registerTypeAdapter(Number.class, new TypeAdapter<Number>() {
public Number read(JsonReader r) throws IOException { return r.nextInt(); } // Integer at runtime
});
// after
public Number read(JsonReader r) throws IOException { return r.nextLong(); } // Long at runtime Defensive patterns
Strategy: try-catch
Try / catch
try {
MyRecord r = gson.fromJson(json, MyRecord.class);
} catch (RuntimeException e) {
if (e.getMessage().contains("Failed to invoke constructor")
&& e.getCause() instanceof IllegalArgumentException) {
// a custom TypeAdapter returned the wrong runtime type
log.error("Type mismatch in record constructor args: {}", e.getCause().getMessage());
}
} Prevention
- Ensure custom TypeAdapters for record component types return the exact declared runtime type
- Unit-test custom adapters in isolation before registering them with Gson
- Avoid broad hierarchy-level adapters (newTypeHierarchyFactory) that can inadvertently match record components
When it happens
Trigger: A registered custom TypeAdapter for a component type returns an object whose class differs from what the record's canonical constructor parameter expects; a broad hierarchy-level adapter inadvertently matches a record component.
Common situations: Registering a TypeAdapter that returns Integer for a Long component; an adapter returning a String for a numeric component; type-erasure-related mismatches in generic adapters.
Related errors
- Could not find the index in the constructor
- Expecting number, got
- Invalid EnumMap type
- Invalid EnumSet type
- null is not allowed as value for record component
AI-assisted analysis of google/gson@310ac341f2 (2026-08-10).
Data as JSON: /api/errors/d6e73ef592d5cc8e.
Report an issue: GitHub.
Appendix: source
Thrown at gson/src/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java:656
+ " same names as the fields in the Java class, and that the order of the"
+ " RecordComponents is the same as the order of the canonical constructor"
+ " parameters.");
}
field.readIntoArray(in, componentIndex, accumulator);
}
@Override
T finalize(Object[] accumulator) {
try {
return constructor.newInstance(accumulator);
} catch (IllegalAccessException e) {
throw ReflectionHelper.createExceptionForUnexpectedIllegalAccess(e);
}
// Note: InstantiationException should be impossible because record class is not abstract;
// IllegalArgumentException should not be possible unless a bad adapter returns objects of
// the wrong type
catch (InstantiationException | IllegalArgumentException e) {
throw new RuntimeException(
"Failed to invoke constructor '"
+ ReflectionHelper.constructorToString(constructor)
+ "' with args "
+ Arrays.toString(accumulator),
e);
} catch (InvocationTargetException e) {
// TODO: JsonParseException ?
throw new RuntimeException(
"Failed to invoke constructor '"
+ ReflectionHelper.constructorToString(constructor)
+ "' with args "
+ Arrays.toString(accumulator),
e.getCause());
}
}
}
}
View on GitHub (pinned to 310ac341f2)