quarkusio/quarkus · error · MessageBundleException
Cannot register [ ] - a localized message bundle interface…
Error message
Cannot register [%s] - a localized message bundle interface exists for locale [%s]: %s
What it means
MessageBundleException thrown at build time when two @Localized subinterfaces of the same @MessageBundle interface declare the same locale. Each locale can map to exactly one localized interface; a duplicate would make message lookup for that locale ambiguous. The build fails listing the new interface, the locale, and the previously registered interface that already holds it.
Solutions
- Change one interface's @Localized value to a distinct locale or regional variant, e.g. @Localized("fr_CA") vs @Localized("fr").
- Delete the duplicate localized interface if it adds nothing beyond the other one.
- Move extra messages into .properties message files for the same locale instead of a second interface.
Example fix
// before
@Localized("fr") public interface AppMessages_fr extends AppMessages { ... }
@Localized("fr") public interface AppMessages_fr2 extends AppMessages { ... }
// after
@Localized("fr") public interface AppMessages_fr extends AppMessages { ... }
@Localized("fr_CA") public interface AppMessages_fr_ca extends AppMessages { ... } Defensive patterns
Strategy: validation
Validate before calling
// ensure each @Localized locale appears at most once per bundle:
Map<String,String> seen = new HashMap<>();
for (Class<?> c : localizedInterfaces) {
String loc = c.getAnnotation(Localized.class).value();
if (seen.containsKey(loc)) throw new IllegalStateException(loc + " already provided by " + seen.get(loc));
seen.put(loc, c.getName());
} Prevention
- Use regional variants (fr vs fr_CA) instead of duplicating a base locale.
- When copying a localized interface, immediately update the @Localized value and class name.
- Prefer .properties message files for additional translations of the same locale rather than a second interface.
- Keep all localized variants of a bundle in the same package so duplicates are easy to spot.
When it happens
Trigger: Two subinterfaces extending the same bundle interface both annotated with the same @Localized value, e.g. @Localized("fr") on AppMessages_fr in two packages, or @Localized("fr") plus @Localized("fr-CA") resolved/colliding as the same locale key in localeToInterface.
Common situations: Copy-pasting a localized interface and forgetting to change @Localized; splitting translations across modules where both define a bundle for the same language; regional variants like fr and fr_CA typos written identically in @Localized.
Related errors
- Locale of [ ] conflicts with the locale [ ] of the default…
- A localized message bundle interface must extend a message…
- A message bundle interface method must not use the key…
- A message bundle method must return java.lang.String
- Bean not found for localized interface
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/070460bc3f24c2f8.
Report an issue: GitHub.
Appendix: source
Thrown at extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/MessageBundleProcessor.java:210
// Find localizations for each interface
String defaultLocale = getDefaultLocale(bundleAnnotation, locales);
List<ClassInfo> localized = new ArrayList<>();
for (ClassInfo implementor : index.getKnownDirectSubinterfaces(bundleClass.name())) {
localized.add(implementor);
}
Map<String, ClassInfo> localeToInterface = new HashMap<>();
for (ClassInfo localizedInterface : localized) {
String locale = localizedInterface.declaredAnnotation(Names.LOCALIZED).value().asString();
if (defaultLocale.equals(locale)) {
throw new MessageBundleException(
String.format(
"Locale of [%s] conflicts with the locale [%s] of the default message bundle [%s]",
localizedInterface, locale, bundleClass));
}
ClassInfo previous = localeToInterface.put(locale, localizedInterface);
if (previous != null) {
throw new MessageBundleException(String.format(
"Cannot register [%s] - a localized message bundle interface exists for locale [%s]: %s",
localizedInterface, locale, previous));
}
localizedInterfaces.add(localizedInterface.name());
}
// Find localized files
Map<String, List<MessageFile>> localeToFiles = new HashMap<>();
// Message templates not specified by a localized interface are looked up in a localized file (merge candidate)
Map<String, List<MessageFile>> localeToMergeCandidates = new HashMap<>();
for (MessageFile messageFile : messageFiles) {
if (messageFile.matchesBundle(name)) {
String locale = messageFile.getLocale(name);
if (locale == null) {
locale = defaultLocale;
}
ClassInfo localizedInterface = localeToInterface.get(locale);
List<MessageFile> files;View on GitHub (pinned to e1c734241f)