quarkusio/quarkus · error · MessageBundleException

A localized message bundle interface must extend a message b

Error message

A localized message bundle interface must extend a message bundle interface: {localized}

What it means

@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.

Source

Thrown at extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/MessageBundleProcessor.java:264

                    // Check for duplicates again
                    checkForDuplicates(localeToMergeCandidates);
                    checkForDuplicates(localeToFiles);

                    bundles.add(new MessageBundleBuildItem(name, bundleClass, localeToInterface,
                            localeToFiles, localeToMergeCandidates, defaultLocale));
                } else {
                    throw new MessageBundleException("@MessageBundle must be declared on an interface: " + bundleClass);
                }
            }
        }

        // Detect interfaces annotated with @Localized that don't extend a message bundle interface
        for (AnnotationInstance localizedAnnotation : index.getAnnotations(Names.LOCALIZED)) {
            if (localizedAnnotation.target().kind() == Kind.CLASS) {
                ClassInfo localized = localizedAnnotation.target().asClass();
                if (Modifier.isInterface(localized.flags())) {
                    if (!localizedInterfaces.contains(localized.name())) {
                        throw new MessageBundleException(
                                "A localized message bundle interface must extend a message bundle interface: " + localized);
                    }
                } else {
                    throw new MessageBundleException("@Localized must be declared on an interface: " + localized);
                }
            }
        }

        // Generate implementations
        // name -> impl class
        Map<String, ClassDesc> generatedImplementations = generateImplementations(bundles, generatedClasses, generatedResources,
                generatedServiceProviders, messageTemplateMethods, index);

        // Register synthetic beans
        for (MessageBundleBuildItem bundle : bundles) {
            ClassInfo bundleInterface = bundle.getDefaultBundleInterface();
            beanRegistration.getContext().configure(bundleInterface.name())
                    .addType(bundle.getDefaultBundleInterface().name())

View on GitHub (pinned to e1c734241f)

Solutions

  1. Make the @Localized interface extend the message bundle interface: `@Localized @Locale("de") interface AppMessagesDe extends AppMessages {}`
  2. Verify the extended interface is annotated with @MessageBundle and is part of the Jandex index (same app or a dependency).
  3. If the interface is meant to be standalone, remove @Localized and annotate it with @MessageBundle instead.

Example fix

// before
@Localized
@Locale("de")
interface AppMessagesDe {
    String hello(String name);
}

// after
@Localized
@Locale("de")
interface AppMessagesDe extends AppMessages {
    @Override
    String hello(String name);
}
Defensive patterns

Strategy: validation

Validate before calling

// Check each @Localized interface extends a bundle interface
for (Class<?> l : localizedInterfaces) {
    if (!l.getAnnotation(Localized.class).isPresent()) continue;
    boolean extendsBundle = java.util.Arrays.stream(l.getInterfaces())
        .anyMatch(p -> p.isAnnotationPresent(MessageBundle.class));
    if (!extendsBundle) throw new IllegalStateException(l + " must extend a message bundle interface");
}

Type guard

static boolean isLocalizedVariant(Class<?> c) {
    return c.isInterface() && c.isAnnotationPresent(Localized.class)
        && java.util.Arrays.stream(c.getInterfaces())
            .anyMatch(p -> p.isAnnotationPresent(MessageBundle.class));
}

Prevention

When it happens

Trigger: 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`.

Common situations: 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.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/ce55c8dcc853d6c5. Report an issue: GitHub.