quarkusio/quarkus · error · java.lang.IllegalStateException

Multiple beans found for localized interface

Error message

Multiple beans found for localized interface [{e.value}] and locale [{e.key}]

What it means

When setting up namespace resolvers, MessageBundles expects exactly one CDI bean per localized interface + locale. If container.select(...) finds multiple matching beans (ambiguous resolution), it throws IllegalStateException('Multiple beans found for localized interface ... and locale ...') instead of guessing.

Solutions

  1. Remove or rename the duplicate localized bundle so only one bean exists per interface+locale
  2. Mark one bean @Alternative/@Priority if intentional overriding is needed
  3. Ensure each @Localized annotation value is unique per bundle interface

Example fix

// before
delete AppMessagesDeCopy.java // duplicate @Localized("de") interface
// after
keep single @Localized("de") AppMessagesDe interface
Defensive patterns

Strategy: validation

Validate before calling

// Detect duplicates before startup: at most one @Localized bean per locale per bundle interface
Map<String,Long> dupes = localizedBeans.stream()
    .collect(Collectors.groupingBy(b -> b.getAnnotation(Localized.class).value(), Collectors.counting()));
dupes.entrySet().stream().filter(e -> e.getValue() > 1).forEach(e -> {
    throw new IllegalStateException("Duplicate @Localized(" + e.getKey() + ") bundle");
});

Try / catch

try {
    startNamespaceResolvers();
} catch (IllegalStateException e) {
    if (e.getMessage().startsWith("Multiple beans found")) {
        throw new IllegalStateException("Remove the duplicate @Localized bundle for this locale", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Two or more beans implementing the same bundle interface carry qualifiers that resolve to the same @Localized locale lookup — e.g. duplicate @Localized("de") interfaces, or an implementation bean added alongside the generated bundle bean.

Common situations: Authoring the same localized bundle twice under different class names; a manual @Produces/@Alternative bean colliding with the generated one; copying bundle interfaces between packages creating duplicate @Localized values.

Related errors


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

Appendix: source

Thrown at extensions/qute/runtime/src/main/java/io/quarkus/qute/i18n/MessageBundles.java:123

        ArcContainer container = Arc.container();
        // For every bundle register a new resolver
        for (Entry<String, Map<String, Class<?>>> entry : context.get().getBundleInterfaces().entrySet()) {
            final String bundleName = entry.getKey();
            final Map<String, Resolver> interfaces = new HashMap<>();
            Resolver resolver = null;
            for (Entry<String, Class<?>> locEntry : entry.getValue().entrySet()) {
                if (locEntry.getKey().equals(DEFAULT_LOCALE)) {
                    resolver = (Resolver) container.select(locEntry.getValue(), Default.Literal.INSTANCE).get();
                    continue;
                }
                Instance<?> found = container.select(locEntry.getValue(), new Localized.Literal(locEntry.getKey()));
                if (found.isUnsatisfied()) {
                    throw new IllegalStateException(
                            Qute.fmt("Bean not found for localized interface [{e.value}] and locale [{e.key}]")
                                    .data("e", locEntry).render());
                }
                if (found.isAmbiguous()) {
                    throw new IllegalStateException(
                            Qute.fmt("Multiple beans found for localized interface [{e.value}] and locale [{e.key}]")
                                    .data("e", locEntry).render());
                }
                interfaces.put(locEntry.getKey(), (Resolver) found.get());
            }
            final Resolver defaultResolver = resolver;

            builder.addNamespaceResolver(new NamespaceResolver() {
                @Override
                public CompletionStage<Object> resolve(EvalContext context) {
                    Object locale = context.getAttribute(ATTRIBUTE_LOCALE);
                    if (locale == null) {
                        Object selectedVariant = context.getAttribute(TemplateInstance.SELECTED_VARIANT);
                        if (selectedVariant != null) {
                            locale = ((Variant) selectedVariant).getLocale();
                        }
                        if (locale == null) {
                            return defaultResolver.resolve(context);

View on GitHub (pinned to e1c734241f)