{"record":{"id":"ce55c8dcc853d6c5","repo":"quarkusio/quarkus","slug":"a-localized-message-bundle-interface-must-extend-a","errorCode":null,"errorMessage":"A localized message bundle interface must extend a message bundle interface: {localized}","messagePattern":"A localized message bundle interface must extend a message bundle interface: (.+?)","errorType":"exception","errorClass":"MessageBundleException","httpStatus":null,"severity":"error","filePath":"extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/MessageBundleProcessor.java","lineNumber":264,"sourceCode":"                    // Check for duplicates again\n                    checkForDuplicates(localeToMergeCandidates);\n                    checkForDuplicates(localeToFiles);\n\n                    bundles.add(new MessageBundleBuildItem(name, bundleClass, localeToInterface,\n                            localeToFiles, localeToMergeCandidates, defaultLocale));\n                } else {\n                    throw new MessageBundleException(\"@MessageBundle must be declared on an interface: \" + bundleClass);\n                }\n            }\n        }\n\n        // Detect interfaces annotated with @Localized that don't extend a message bundle interface\n        for (AnnotationInstance localizedAnnotation : index.getAnnotations(Names.LOCALIZED)) {\n            if (localizedAnnotation.target().kind() == Kind.CLASS) {\n                ClassInfo localized = localizedAnnotation.target().asClass();\n                if (Modifier.isInterface(localized.flags())) {\n                    if (!localizedInterfaces.contains(localized.name())) {\n                        throw new MessageBundleException(\n                                \"A localized message bundle interface must extend a message bundle interface: \" + localized);\n                    }\n                } else {\n                    throw new MessageBundleException(\"@Localized must be declared on an interface: \" + localized);\n                }\n            }\n        }\n\n        // Generate implementations\n        // name -> impl class\n        Map<String, ClassDesc> generatedImplementations = generateImplementations(bundles, generatedClasses, generatedResources,\n                generatedServiceProviders, messageTemplateMethods, index);\n\n        // Register synthetic beans\n        for (MessageBundleBuildItem bundle : bundles) {\n            ClassInfo bundleInterface = bundle.getDefaultBundleInterface();\n            beanRegistration.getContext().configure(bundleInterface.name())\n                    .addType(bundle.getDefaultBundleInterface().name())","sourceCodeStart":246,"sourceCodeEnd":282,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/MessageBundleProcessor.java#L246-L282","documentation":"@Localized marks an interface as a translated variant of a message bundle for a specific locale. The processor requires that any @Localized interface actually extends a bundle interface (one carrying @MessageBundle). If the index shows the interface does not extend a known bundle interface, the build fails so translations can never be attached to a nonexistent bundle.","triggerScenarios":"Annotating an interface with @Localized (and @Locale) when it does not extend an interface annotated with @MessageBundle, e.g. `@Localized @Locale(\"de\") interface AppMessagesDe {}` with no `extends AppMessages`.","commonSituations":"Renaming the base bundle interface but forgetting to update the extends clause; creating a localized interface from scratch by copying only the annotations; annotation moved to a standalone interface during refactoring.","solutions":["Make the @Localized interface extend the message bundle interface: `@Localized @Locale(\"de\") interface AppMessagesDe extends AppMessages {}`","Verify the extended interface is annotated with @MessageBundle and is part of the Jandex index (same app or a dependency).","If the interface is meant to be standalone, remove @Localized and annotate it with @MessageBundle instead."],"exampleFix":"// before\n@Localized\n@Locale(\"de\")\ninterface AppMessagesDe {\n    String hello(String name);\n}\n\n// after\n@Localized\n@Locale(\"de\")\ninterface AppMessagesDe extends AppMessages {\n    @Override\n    String hello(String name);\n}","handlingStrategy":"validation","validationCode":"// Check each @Localized interface extends a bundle interface\nfor (Class<?> l : localizedInterfaces) {\n    if (!l.getAnnotation(Localized.class).isPresent()) continue;\n    boolean extendsBundle = java.util.Arrays.stream(l.getInterfaces())\n        .anyMatch(p -> p.isAnnotationPresent(MessageBundle.class));\n    if (!extendsBundle) throw new IllegalStateException(l + \" must extend a message bundle interface\");\n}","typeGuard":"static boolean isLocalizedVariant(Class<?> c) {\n    return c.isInterface() && c.isAnnotationPresent(Localized.class)\n        && java.util.Arrays.stream(c.getInterfaces())\n            .anyMatch(p -> p.isAnnotationPresent(MessageBundle.class));\n}","tryCatchPattern":null,"preventionTips":["Always write `interface XxxDe extends XxxBundle` when adding a locale","Never create standalone @Localized interfaces","Generate localized skeletons from the default bundle to keep hierarchy intact"],"tags":["quarkus","qute","message-bundle","localization"],"backgroundTag":"message-bundle-annotation-misuse","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"}