{"record":{"id":"9592c5bb650a60be","repo":"quarkusio/quarkus","slug":"template-extension-methods-that-share-the-namespac","errorCode":null,"errorMessage":"Template extension methods that share the namespace <namespace> must be declared on the same class; but declared on <class1> and <class2>","messagePattern":"Template extension methods that share the namespace <namespace> must be declared on the same class; but declared on <class1> and <class2>","errorType":"exception","errorClass":"java.lang.IllegalStateException","httpStatus":null,"severity":"error","filePath":"extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java","lineNumber":2100,"sourceCode":"        Map<DotName, Map<String, List<TemplateExtensionMethodBuildItem>>> classToNamespaceExtensions = new HashMap<>();\n        Map<String, DotName> namespaceToClass = new HashMap<>();\n\n        for (TemplateExtensionMethodBuildItem templateExtension : templateExtensionMethods) {\n            String generatedValueResolverClass = existingValueResolvers.getGeneratedClass(templateExtension.getMethod());\n            if (generatedValueResolverClass != null) {\n                // A ValueResolver of a non-application class was already generated\n                generatedValueResolvers.add(generatedValueResolverClass);\n                continue;\n            }\n\n            if (templateExtension.hasNamespace()) {\n                // Group extension methods declared on the same class by namespace\n                DotName declaringClassName = templateExtension.getMethod().declaringClass().name();\n                DotName namespaceClassName = namespaceToClass.get(templateExtension.getNamespace());\n                if (namespaceClassName == null) {\n                    namespaceToClass.put(templateExtension.getNamespace(), namespaceClassName);\n                } else if (!namespaceClassName.equals(declaringClassName)) {\n                    throw new IllegalStateException(\"Template extension methods that share the namespace \"\n                            + templateExtension.getNamespace() + \" must be declared on the same class; but declared on \"\n                            + namespaceClassName + \" and \" + declaringClassName);\n                }\n                Map<String, List<TemplateExtensionMethodBuildItem>> namespaceToExtensions = classToNamespaceExtensions\n                        .get(declaringClassName);\n                if (namespaceToExtensions == null) {\n                    namespaceToExtensions = new HashMap<>();\n                    classToNamespaceExtensions.put(declaringClassName, namespaceToExtensions);\n                }\n                List<TemplateExtensionMethodBuildItem> namespaceMethods = namespaceToExtensions\n                        .get(templateExtension.getNamespace());\n                if (namespaceMethods == null) {\n                    namespaceMethods = new ArrayList<>();\n                    namespaceToExtensions.put(templateExtension.getNamespace(), namespaceMethods);\n                }\n                namespaceMethods.add(templateExtension);\n            } else {\n                // Generate ValueResolver per extension method","sourceCodeStart":2082,"sourceCodeEnd":2118,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java#L2082-L2118","documentation":"Thrown when two or more template extension methods declare the same @TemplateExtension namespace but are declared on different classes. Qute requires all extension methods of a given namespace to live on a single class so namespace resolution is unambiguous.","triggerScenarios":"Two helper classes both annotated methods with @TemplateExtension(namespace = \"cfg\") or matching value; a namespace collision introduced after moving one extension method to a shared library class while a local copy remains.","commonSituations":"Splitting extension methods across utility classes during refactoring; two dependencies each defining extensions in the same custom namespace; copy-pasting an extension class and forgetting to change the namespace.","solutions":["Move all methods using the same namespace into one class","Change the namespace on one of the colliding classes to a distinct namespace","Remove one of the duplicate extension methods if it is redundant","If it comes from a third-party dependency, exclude/override that dependency or pick a new namespace in your code"],"exampleFix":"// before\nclass A { @TemplateExtension(namespace=\"money\") static String fmt(BigDecimal v){...} }\nclass B { @TemplateExtension(namespace=\"money\") static String shortFmt(BigDecimal v){...} }\n\n// after\nclass A { @TemplateExtension(namespace=\"money\")\n  static String fmt(BigDecimal v){...}\n  static String shortFmt(BigDecimal v){...} }","handlingStrategy":"validation","validationCode":"// Ensure each custom namespace is declared on exactly one class\nMap<String, Long> nsCount = allExtensionClasses.stream()\n    .flatMap(c -> c.namespaces().stream())\n    .collect(Collectors.groupingBy(n -> n, Collectors.counting()));\nList<String> dupes = nsCount.entrySet().stream().filter(e -> e.getValue() > 1).map(Map.Entry::getKey).toList();","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep all methods of a namespace in a single dedicated class","Prefix namespaces with your app name to avoid dependency collisions","Grep for @TemplateExtension(namespace = ...) when introducing a new namespace","Document namespace ownership in team conventions"],"tags":["quarkus","qute","namespace-collision","template-extension-method"],"backgroundTag":"namespace-collision","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}