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

  1. Inspect the caused-by IllegalArgumentException for the specific type mismatch
  2. Verify every custom TypeAdapter registered for component types returns the correct runtime type
  3. 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

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


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)