{"record":{"id":"2456dfe93d67f2e2","repo":"OpenAPITools/openapi-generator","slug":"failed-to-instantiate-custom-normalizer-class-cl","errorCode":null,"errorMessage":"Failed to instantiate custom NORMALIZER_CLASS '{className}'. The class was found but could not be constructed; it must declare a public constructor accepting (OpenAPI, Map<String, String>) and that constructor must not throw.","messagePattern":"Failed to instantiate custom NORMALIZER_CLASS '(.+?)'\\. The class was found but could not be constructed; it must declare a public constructor accepting \\(OpenAPI, Map<String, String>\\) and that constructor must not throw\\.","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java","lineNumber":201,"sourceCode":"    public static OpenAPINormalizer createNormalizer(OpenAPI openAPI, Map<String, String> inputRules) {\n        if (inputRules.containsKey(NORMALIZER_CLASS)) {\n            String className = inputRules.get(NORMALIZER_CLASS);\n            Class<?> clazz;\n            try {\n                clazz = loadNormalizerClass(className);\n            } catch (ClassNotFoundException e) {\n                throw new RuntimeException(\n                        \"Failed to load custom \" + NORMALIZER_CLASS + \" '\" + className + \"'. This class must be \"\n                                + \"visible on the generation runtime classpath (i.e. resolvable either by the \"\n                                + \"current thread's context classloader or by the classloader that loaded \"\n                                + \"openapi-generator itself). Ensure the class (and its dependencies) is on the \"\n                                + \"classpath used to launch the generator.\", e);\n            }\n            try {\n                Constructor<?> constructor = clazz.getConstructor(OpenAPI.class, Map.class);\n                return (OpenAPINormalizer) constructor.newInstance(openAPI, inputRules);\n            } catch (ReflectiveOperationException e) {\n                throw new RuntimeException(\n                        \"Failed to instantiate custom \" + NORMALIZER_CLASS + \" '\" + className + \"'. The class was \"\n                                + \"found but could not be constructed; it must declare a public constructor \"\n                                + \"accepting (OpenAPI, Map<String, String>) and that constructor must not throw.\", e);\n            }\n        } else {\n            return new OpenAPINormalizer(openAPI, inputRules);\n        }\n    }\n\n    /**\n     * Loads a custom normalizer class, preferring the current thread's context classloader (which\n     * frameworks such as Gradle's Worker API set to a classloader that includes any user-supplied\n     * classpath) and falling back to the classloader that defined {@link OpenAPINormalizer} itself\n     * (the original, pre-existing behavior) so that normalizers already visible on the default\n     * classpath keep working unchanged.\n     *\n     * @param className fully qualified name of the custom {@link OpenAPINormalizer} subclass\n     * @return the resolved {@link Class}","sourceCodeStart":183,"sourceCodeEnd":219,"githubUrl":"https://github.com/OpenAPITools/openapi-generator/blob/fcec517be3cf5b7964296bcba25fbc97541484e7/modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java#L183-L219","documentation":"The companion of error [13]: OpenAPINormalizer.createNormalizer DID load your NORMALIZER_CLASS, but the reflective construction failed - clazz.getConstructor(OpenAPI.class, Map.class) or constructor.newInstance(openAPI, inputRules) threw a ReflectiveOperationException. Concretely: no public constructor with exactly (OpenAPI, Map) parameters (NoSuchMethodException), the constructor exists but is not accessible (IllegalAccessException), or it exists and ran but THREW (InvocationTargetException - see the chained cause). The class must also be assignable to OpenAPINormalizer to survive the cast.","triggerScenarios":"Declaring the normalizer constructor as (OpenAPI, Map<String,String>) with generics is fine, but adding/omitting a parameter, making it protected/private, throwing IllegalArgumentException on an unrecognized rule inside the constructor, or doing IO in the constructor that fails - each produces this error right after the class loaded successfully.","commonSituations":"First custom normalizers modelled on examples that validate rules eagerly in the constructor; refactoring a normalizer's constructor signature and not re-checking the contract; constructors reading files/system properties that are absent in CI.","solutions":["Declare exactly one public constructor: public MyNormalizer(OpenAPI openAPI, Map<String, String> rules) - note raw Map in the signature is what getConstructor matches.","If InvocationTargetException, open the chained 'Caused by:' - your constructor threw; move risky parsing/IO out of the constructor (do it lazily on first normalize call) or make unknown rules non-fatal.","Verify the class extends OpenAPINormalizer (or at least is one) so the cast after newInstance succeeds.","Add a one-line unit test that reflectively calls new MyNormalizer(new OpenAPI(), Map.of()) so signature drift breaks the build, not generation."],"exampleFix":"// before\npublic class MyNormalizer extends OpenAPINormalizer {\n    public MyNormalizer() { super(new OpenAPI(), Map.of()); } // wrong signature\n}\n// after\npublic class MyNormalizer extends OpenAPINormalizer {\n    public MyNormalizer(OpenAPI openAPI, Map<String, String> rules) {\n        super(openAPI, rules);\n    }\n}","handlingStrategy":"validation","validationCode":"// Verify the required constructor exists and is public before configuring the rule\nClass<?> c = Class.forName(\"com.example.MyNormalizer\");\njava.lang.reflect.Constructor<?> ctor;\ntry {\n    ctor = c.getConstructor(io.swagger.v3.oas.models.OpenAPI.class, java.util.Map.class);\n} catch (NoSuchMethodException e) {\n    throw new IllegalStateException(\n        \"MyNormalizer needs: public MyNormalizer(OpenAPI, Map<String,String>)\", e);\n}\nif (!java.lang.reflect.Modifier.isPublic(ctor.getModifiers())) {\n    throw new IllegalStateException(\"MyNormalizer constructor must be public\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    OpenAPINormalizer.createNormalizer(openAPI, rules);\n} catch (RuntimeException e) {\n    if (e.getMessage() != null && e.getMessage().startsWith(\"Failed to instantiate custom NORMALIZER_CLASS\")) {\n        Throwable root = e.getCause() != null ? e.getCause().getCause() : null; // InvocationTargetException target\n        // root != null => constructor threw; fix that inner failure, not the reflection call\n        throw new IllegalStateException(\"Normalizer constructor failed\", root != null ? root : e);\n    }\n    throw e;\n}","preventionTips":["Keep a unit test that reflectively invokes new MyNormalizer(new OpenAPI(), Map.of()) so signature drift fails the build.","Do no IO or strict validation inside the normalizer constructor; defer to first use.","Extend OpenAPINormalizer so the post-instantiation cast always succeeds."],"tags":["openapi-generator","reflection","custom-normalizer","constructor","java"],"backgroundTag":"reflection-class-instantiation-failure","analyzedSha":"fcec517be3cf5b7964296bcba25fbc97541484e7","analyzedAt":"2026-08-22T11:13:11.613Z","schemaVersion":2},"datasetVersion":"2026-08-22T14:17:55.899Z"}