quarkusio/quarkus · error · IllegalStateException

Multiple instances of %1$s were found for Hibernate Search S

Error message

Multiple instances of %1$s were found for Hibernate Search Standalone. At most one instance can be assigned. Instances found: %2$s

What it means

Global-scope variant of the ambiguity check: when neither an index nor backend name applies, singleExtensionBeanReferenceFor() requires exactly one bean of beanType in the whole Hibernate Search Standalone context. If multiple globally-qualified beans of that type exist, it throws IllegalStateException enumerating them.

Source

Thrown at extensions/hibernate-search-standalone-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/standalone/elasticsearch/runtime/bean/HibernateSearchBeanUtil.java:44

    private static <T> Optional<BeanReference<T>> singleExtensionBeanReferenceFor(Class<T> beanType,
            String backendName, String indexName) {
        InjectableInstance<T> instance = extensionInstanceFor(beanType, backendName, indexName);
        if (instance.isAmbiguous()) {
            List<String> ambiguousClassNames = instance.handlesStream().map(h -> h.getBean().getBeanClass().getCanonicalName())
                    .toList();
            if (indexName != null) {
                throw new IllegalStateException(String.format(Locale.ROOT,
                        "Multiple instances of %1$s were found for Hibernate Search Standalone index %2$s."
                                + " At most one instance can be assigned to each index. Instances found: %3$s",
                        beanType.getSimpleName(), indexName, ambiguousClassNames));
            } else if (backendName != null) {
                throw new IllegalStateException(String.format(Locale.ROOT,
                        "Multiple instances of %1$s were found for Hibernate Search Standalone backend %2$s."
                                + " At most one instance can be assigned to each backend. Instances found: %3$s",
                        beanType.getSimpleName(), backendName, ambiguousClassNames));
            } else {
                throw new IllegalStateException(String.format(Locale.ROOT,
                        "Multiple instances of %1$s were found for Hibernate Search Standalone."
                                + " At most one instance can be assigned. Instances found: %2$s",
                        beanType.getSimpleName(), ambiguousClassNames));
            }
        }
        return instance.isResolvable() ? Optional.of(new ArcBeanReference<>(instance.getHandle().getBean())) : Optional.empty();
    }

    public static <T> Optional<List<BeanReference<T>>> multiExtensionBeanReferencesFor(Optional<List<String>> override,
            Class<T> beanType,
            String backendName, String indexName) {
        return override.map(strings -> strings.stream()
                .map(string -> BeanReference.parse(beanType, string))
                .collect(Collectors.toList()))
                .or(() -> multiExtensionBeanReferencesFor(beanType, backendName, indexName));
    }

    private static <T> Optional<List<BeanReference<T>>> multiExtensionBeanReferencesFor(Class<T> beanType,

View on GitHub (pinned to e1c734241f)

Solutions

  1. Remove one of the globally-scoped beans listed in the 'Instances found' message.
  2. Target each bean to a specific backend or index via @HibernateSearchExtension(backend=.../index=...) so the global lookup resolves uniquely.
  3. Mark the undesired bean as @Vetoed or remove its bean-defining annotation so ArC ignores it.
  4. If intentional replacement is wanted, use @Alternative + @Priority instead of registering two beans side by side.

Example fix

// before: two global beans of the same type
@HibernateSearchExtension
@ApplicationScoped public class ObserverA implements MassIndexingObserver { ... }
@HibernateSearchExtension
@ApplicationScoped public class ObserverB implements MassIndexingObserver { ... }

// after: keep one, or target the other
@HibernateSearchExtension
@ApplicationScoped public class ObserverA implements MassIndexingObserver { ... }
// ObserverB deleted (or annotated with @Vetoed)
Defensive patterns

Strategy: validation

Validate before calling

// Global-scope ambiguity check
InjectableInstance<MyType> i = Arc.container().select(MyType.class, hibernateSearchExtensionQualifier);
if (i.isAmbiguous()) {
    throw new IllegalStateException("Multiple global Hibernate Search beans of " + MyType.class);
}

Type guard

public static <T> boolean hasSingleGlobalBean(Class<T> type, Annotation... qualifiers) {
    var instance = Arc.container().select(type, qualifiers);
    return instance.isResolvable() && !instance.isAmbiguous();
}

Try / catch

try {
    Optional<BeanReference<T>> ref = HibernateSearchBeanUtil.singleExtensionBeanReferenceFor(beanType, null, null);
} catch (IllegalStateException e) {
    log.errorf("Multiple global Hibernate Search Standalone beans found — keep one or add backend/index qualifiers: %s", e.getMessage(), e);
}

Prevention

When it happens

Trigger: Two or more beans of beanType carry @HibernateSearchExtension without backend/index targeting (global scope), so the plain lookup is ambiguous and instance.isAmbiguous() fires.

Common situations: Registering two global customizations of the same role (e.g. two mass-indexing observers or two mapper beans) both annotated without scope qualifiers; leaving a test/alternative bean (@Alternative not active or both discovered) in the archive; adding an extension default bean plus your own global bean.

Related errors


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