quarkusio/quarkus · error · MessageBundleException
Cannot register [%s] - 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.
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)
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 [%s] conflicts with the locale [%s] of the default
- Message bundle name [%s] declared on %s must be a valid name
- Message bundle interface name conflict - [%s] is used for bo
- @MessageBundle must be declared on an interface: {bundleClas
- A localized message bundle interface must extend a message b
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/070460bc3f24c2f8.
Report an issue: GitHub.