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

  1. Change one interface's @Localized value to a distinct locale or regional variant, e.g. @Localized("fr_CA") vs @Localized("fr").
  2. Delete the duplicate localized interface if it adds nothing beyond the other one.
  3. 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

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


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