{"record":{"id":"0e7f46ded2ecf9c0","repo":"OpenAPITools/openapi-generator","slug":"failed-to-load-custom-normalizer-class-classname","errorCode":null,"errorMessage":"Failed to load custom NORMALIZER_CLASS '{className}'. This class must be visible on the generation runtime classpath (i.e. resolvable either by the current thread's context classloader or by the classloader that loaded openapi-generator itself). Ensure the class (and its dependencies) is on the classpath used to launch the generator.","messagePattern":"Failed to load custom NORMALIZER_CLASS '(.+?)'\\. This class must be visible on the generation runtime classpath \\(i\\.e\\. resolvable either by the current thread's context classloader or by the classloader that loaded openapi-generator itself\\)\\. Ensure the class \\(and its dependencies\\) is on the classpath used to launch the generator\\.","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java","lineNumber":190,"sourceCode":"    final String LOOSE_NULL_DEFINITIONS = \"LOOSE_NULL_DEFINITIONS\";\n\n    // ============= end of rules =============\n\n    private static final String ONE_OF_ANY_OF_ENUM_SIMPLIFIED = \"Simplified {} with enum sub-schemas to single enum: {} since rule {} was enabled\";\n\n    /**\n     * Factory constructor for OpenAPINormalizer.\n     *\n     * Default can be overriden by setting the NORMALIZER_CLASS rule\n     */\n    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        }","sourceCodeStart":172,"sourceCodeEnd":208,"githubUrl":"https://github.com/OpenAPITools/openapi-generator/blob/fcec517be3cf5b7964296bcba25fbc97541484e7/modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java#L172-L208","documentation":"OpenAPINormalizer.createNormalizer handles the NORMALIZER_CLASS rule: instead of built-in rules it reflectively loads a user-supplied class via loadNormalizerClass (thread context classloader first, then openapi-generator's own loader - deliberately friendly to Gradle Worker API isolation). ClassNotFoundException is rethrown with this guidance: the class must be visible to the classloader of the RUNNING generator process, i.e. on the launch classpath, not merely on your project's compile classpath.","triggerScenarios":"Passing --openapi-normalizer NORMALIZER_CLASS=com.example.MyNormalizer where com.example.MyNormalizer is not on the runtime classpath: using the standalone openapi-generator-cli jar without your classes; Maven plugin without the normalizer artifact declared inside the PLUGIN's <dependencies>; Gradle setup where the worker action classloader cannot see the class; simple FQCN typo or stale package after a rename.","commonSituations":"Writing a first custom normalizer and testing via the downloaded CLI; adding the dependency to the project instead of the maven-plugin block; renaming packages and forgetting the option string; shaded jars built without the normalizer module.","solutions":["Fix the fully-qualified class name first: it must match package + class exactly (no spaces, correct casing).","Get the class onto the GENERATOR's runtime classpath: with the Maven plugin, declare the normalizer artifact inside the plugin's <dependencies> block (not the project's); with the CLI, launch via java -cp 'your-normalizer.jar:openapi-generator-cli.jar' org.openapitools.codegen.OpenAPIGenerator or build an uber-jar including it.","In embedded/Gradle contexts, set the thread context classloader (Thread.currentThread().setContextClassLoader(...)) to a loader that sees the class before invoking generation, matching what loadNormalizerClass prefers.","Verify visibility at runtime before configuring: Class.forName(name, false, Thread.currentThread().getContextClassLoader()) in a smoke test."],"exampleFix":"<!-- before: dependency only on the project, plugin can't see it -->\n<plugin>\n  <groupId>org.openapitools</groupId>\n  <artifactId>openapi-generator-maven-plugin</artifactId>\n</plugin>\n<!-- after: dependency inside the plugin block -->\n<plugin>\n  <groupId>org.openapitools</groupId>\n  <artifactId>openapi-generator-maven-plugin</artifactId>\n  <dependencies>\n    <dependency>\n      <groupId>com.example</groupId>\n      <artifactId>my-normalizer</artifactId>\n      <version>1.0.0</version>\n    </dependency>\n  </dependencies>\n</plugin>","handlingStrategy":"validation","validationCode":"// Verify the normalizer class is visible to the runtime BEFORE configuring it\nString className = \"com.example.MyNormalizer\";\ntry {\n    Class.forName(className, false, Thread.currentThread().getContextClassLoader());\n} catch (ClassNotFoundException ctxMissing) {\n    try {\n        Class.forName(className, false, OpenAPINormalizer.class.getClassLoader());\n    } catch (ClassNotFoundException coreMissing) {\n        throw new IllegalStateException(\n            \"NORMALIZER_CLASS not on generator runtime classpath: \" + className);\n    }\n}\nMap<String, String> rules = Map.of(\"NORMALIZER_CLASS\", className);","typeGuard":null,"tryCatchPattern":"try {\n    OpenAPINormalizer.createNormalizer(openAPI, rules);\n} catch (RuntimeException e) {\n    if (e.getMessage() != null && e.getMessage().startsWith(\"Failed to load custom NORMALIZER_CLASS\")) {\n        // classpath issue: add jar to plugin/CLI classpath; rethrow with remediation\n        throw new IllegalStateException(\"Add normalizer jar to the generator's classpath\", e);\n    }\n    throw e;\n}","preventionTips":["Declare custom-normalizer artifacts inside the openapi-generator-maven-plugin <dependencies> block.","For CLI use, launch with java -cp including your normalizer jar rather than the bare downloaded CLI.","Pin the normalizer's openapi-generator dependency to the running version."],"tags":["openapi-generator","classpath","reflection","custom-normalizer","maven-plugin"],"backgroundTag":"class-not-found-classpath","analyzedSha":"fcec517be3cf5b7964296bcba25fbc97541484e7","analyzedAt":"2026-08-22T11:13:11.613Z","schemaVersion":2},"datasetVersion":"2026-08-22T14:17:55.899Z"}