{"record":{"id":"5e00ca5eb08fa294","repo":"quarkusio/quarkus","slug":"template-builder-already-specified-for-rootcaus","errorCode":null,"errorMessage":"Template builder already specified for: ${rootCauseClassName}","messagePattern":"Template builder already specified for: (.+?)","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"core/devmode-spi/src/main/java/io/quarkus/dev/ErrorPageGenerators.java","lineNumber":26,"sourceCode":" * The generators can be used to generate a custom HTML page for a specific deployment exception that occurs during the\n * development mode.\n * <p>\n * In order to avoid classloading issues the generators should not access the root cause directly but use reflection instead\n * (the exception class could be loaded by a different class loader).\n */\npublic class ErrorPageGenerators {\n\n    private static final Map<String, Function<Throwable, String>> generators = new ConcurrentHashMap<>();\n\n    /**\n     * Register a function that will be used to generate the error page for the given root cause.\n     *\n     * @param rootCauseClassName\n     * @param function\n     */\n    public static void register(String rootCauseClassName, Function<Throwable, String> function) {\n        if (generators.putIfAbsent(rootCauseClassName, function) != null) {\n            throw new IllegalStateException(\"Template builder already specified for: \" + rootCauseClassName);\n        }\n    }\n\n    public static Function<Throwable, String> get(String rootCauseClassName) {\n        return generators.get(rootCauseClassName);\n    }\n\n    // This method is called by a relevant service provider during HotReplacementSetup#close()\n    public static void clear() {\n        generators.clear();\n    }\n\n}\n","sourceCodeStart":8,"sourceCodeEnd":40,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/core/devmode-spi/src/main/java/io/quarkus/dev/ErrorPageGenerators.java#L8-L40","documentation":"ErrorPageGenerators.register() maps a root-cause exception class name to a function that renders a custom error page in dev mode. Only one generator per class is allowed; registering a second generator for the same class name throws this IllegalStateException.","triggerScenarios":"Calling ErrorPageGenerators.register(className, fn) twice with the same rootCauseClassName — e.g. two extensions or a hot-redeployed classpath registering the same exception handler again.","commonSituations":"Two extensions both registering handlers for the same exception type; dev-mode hot reload re-invoking registration code; user code registering a custom handler that collides with an extension's handler.","solutions":["Remove one of the duplicate register() calls for the same class name","Guard your registration with ErrorPageGenerators.get(className) == null before registering","On hot reload, make registration idempotent (check before register)"],"exampleFix":"// before\nErrorPageGenerators.register(\"com.acme.MyException\", fn);\n// after\nif (ErrorPageGenerators.get(\"com.acme.MyException\") == null) {\n    ErrorPageGenerators.register(\"com.acme.MyException\", fn);\n}","handlingStrategy":"validation","validationCode":"String cls = \"com.acme.MyException\";\nif (ErrorPageGenerators.get(cls) == null) {\n    ErrorPageGenerators.register(cls, fn);\n}","typeGuard":null,"tryCatchPattern":"try { ErrorPageGenerators.register(cls, fn); } catch (IllegalStateException e) { if (e.getMessage().startsWith(\"Template builder already specified\")) { /* skip or replace existing registration */ } else { throw e; } }","preventionTips":["Check get(className) before register()","Coordinate with extensions that may register the same exception","Make registration idempotent for hot-reload cycles"],"tags":["quarkus","dev-mode","error-page","duplicate-registration"],"backgroundTag":"duplicate-registration","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}