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
- 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.
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
- 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
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
- @Localized must be declared on an interface: {localized}
- Default bundle method not found on %s: %s
- Message bundle name [%s] declared on %s must be a valid name
- Message bundle interface name conflict - [%s] is used for bo
- Locale of [%s] conflicts with the locale [%s] of the default
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/ce55c8dcc853d6c5.
Report an issue: GitHub.