{"id":"932429a22778aa2b","repo":"google/gson","slug":"failed-to-invoke-constructor-reflectionhelper","errorCode":null,"errorMessage":"Failed to invoke constructor '\" + ReflectionHelper.constructorToString(constructor) + \"' with args \" + Arrays.toString(accumulator)","messagePattern":"Failed to invoke constructor '\" \\+ ReflectionHelper\\.constructorToString\\(constructor\\) \\+ \"' with args \" \\+ Arrays\\.toString\\(accumulator\\)","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"gson/src/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java","lineNumber":656,"sourceCode":"                + \" same names as the fields in the Java class, and that the order of the\"\n                + \" RecordComponents is the same as the order of the canonical constructor\"\n                + \" parameters.\");\n      }\n      field.readIntoArray(in, componentIndex, accumulator);\n    }\n\n    @Override\n    T finalize(Object[] accumulator) {\n      try {\n        return constructor.newInstance(accumulator);\n      } catch (IllegalAccessException e) {\n        throw ReflectionHelper.createExceptionForUnexpectedIllegalAccess(e);\n      }\n      // Note: InstantiationException should be impossible because record class is not abstract;\n      //  IllegalArgumentException should not be possible unless a bad adapter returns objects of\n      //  the wrong type\n      catch (InstantiationException | IllegalArgumentException e) {\n        throw new RuntimeException(\n            \"Failed to invoke constructor '\"\n                + ReflectionHelper.constructorToString(constructor)\n                + \"' with args \"\n                + Arrays.toString(accumulator),\n            e);\n      } catch (InvocationTargetException e) {\n        // TODO: JsonParseException ?\n        throw new RuntimeException(\n            \"Failed to invoke constructor '\"\n                + ReflectionHelper.constructorToString(constructor)\n                + \"' with args \"\n                + Arrays.toString(accumulator),\n            e.getCause());\n      }\n    }\n  }\n}\n","sourceCodeStart":638,"sourceCodeEnd":674,"githubUrl":"https://github.com/google/gson/blob/8b8628c65699bc4421696183c62ae0c1b9b281dc/gson/src/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java#L638-L674","documentation":"Thrown by RecordAdapter.finalize when invoking the record's canonical constructor fails with IllegalAccessException or InstantiationException or IllegalArgumentException (wrapped into one catch). The first two should be near-impossible per Gson's own comments; IllegalArgumentException means a registered adapter returned an object of the wrong type for a constructor argument. Wrapped as a RuntimeException carrying the original exception.","triggerScenarios":"A custom TypeAdapter registered for a record component returns a value whose type does not match the canonical constructor parameter (e.g., returns a String for an int parameter); a security manager or module layer denies constructor access after the initial makeAccessible; subclassing tools that make the record abstract. Catch site: line 655.","commonSituations":"Mixing custom adapters for boxed/primitive components incorrectly; JPMS layer changes after adapter construction; security managers rejecting reflection; buggy generic TypeAdapter returning wrong runtime types.","solutions":["Inspect the wrapped exception: IllegalArgumentException -> fix the custom adapter for the named component type.","Ensure the canonical constructor parameter types match what your TypeAdapters produce.","Open the package containing the record in module-info so reflective construction is permitted.","Register a full TypeAdapter for the record so Gson never invokes the canonical constructor reflectively."],"exampleFix":"// before: adapter returns wrong type for primitive component\nregisterTypeAdapter(int.class, new TypeAdapter<Integer>(){\n  public Integer read(JsonReader r){ return r.nextString(); } // String for int\n});\n\n// after\npublic Integer read(JsonReader r){ return r.nextInt(); }","handlingStrategy":"try-catch","validationCode":"// Ensure adapters for record components return assignable types\nfor (RecordComponent rc : MyRecord.class.getRecordComponents()) {\n  TypeAdapter<?> a = gson.getAdapter(TypeToken.get(rc.getType()));\n  // read a sample and check class before relying on it\n}","typeGuard":"null","tryCatchPattern":"try {\n  gson.fromJson(json, MyRecord.class);\n} catch (RuntimeException e) {\n  if (e.getMessage().startsWith(\"Failed to invoke constructor\") && !(e.getCause() instanceof InvocationTargetException)) {\n    // IllegalAccessException / IllegalArgumentException: fix adapter return types\n  } else throw e;\n}","preventionTips":["Unit-test each custom TypeAdapter against the exact parameter type of the record constructor.","Open the record's package to Gson in module-info to avoid IllegalAccessException.","Keep primitive and boxed adapter registrations distinct and correct.","Prefer InstanceCreator or full record TypeAdapters when component types are non-trivial."],"tags":["gson","record","constructor","typeadapter","deserialization"],"analyzedSha":"8b8628c65699bc4421696183c62ae0c1b9b281dc","analyzedAt":"2026-08-04T19:12:22.202Z","schemaVersion":2}