quarkusio/quarkus · error · IllegalStateException

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

Error message

Multiple instances of %1$s were found for Hibernate Search backend %2$s in persistence unit %3$s. At most one instance can be assigned to each backend. Instances found: %4$s

What it means

Same resolution failure as the index-level variant, but scoped to an entire Hibernate Search backend: HibernateSearchBeanUtil.singleExtensionBeanReferenceFor found more than one CDI bean of the requested type for the named backend (indexName was null, backendName was non-null) in the given persistence unit. At most one instance may be assigned per backend, so the extension throws IllegalStateException listing all matching bean classes.

Source

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

    public static <T> Optional<BeanReference<T>> singleExtensionBeanReferenceFor(Optional<String> override, Class<T> beanType,
            String persistenceUnitName, String backendName, String indexName) {
        return override.map(string -> BeanReference.parse(beanType, string))
                .or(() -> singleExtensionBeanReferenceFor(beanType, persistenceUnitName, backendName, indexName));
    }

    private static <T> Optional<BeanReference<T>> singleExtensionBeanReferenceFor(Class<T> beanType,
            String persistenceUnitName, String backendName, String indexName) {
        InjectableInstance<T> instance = extensionInstanceFor(beanType, persistenceUnitName, 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 index %2$s in persistence unit %3$s."
                                + " At most one instance can be assigned to each index. Instances found: %4$s",
                        beanType.getSimpleName(), indexName, persistenceUnitName, ambiguousClassNames));
            } else if (backendName != null) {
                throw new IllegalStateException(String.format(Locale.ROOT,
                        "Multiple instances of %1$s were found for Hibernate Search backend %2$s in persistence unit %3$s."
                                + " At most one instance can be assigned to each backend. Instances found: %4$s",
                        beanType.getSimpleName(), backendName, persistenceUnitName, ambiguousClassNames));
            } else {
                throw new IllegalStateException(String.format(Locale.ROOT,
                        "Multiple instances of %1$s were found for Hibernate Search in persistence unit %2$s."
                                + " At most one instance can be assigned to each persistence unit. Instances found: %3$s",
                        beanType.getSimpleName(), persistenceUnitName, 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 persistenceUnitName, String backendName, String indexName) {
        return override.map(strings -> strings.stream()
                .map(string -> BeanReference.parse(beanType, string))

View on GitHub (pinned to e1c734241f)

Solutions

  1. Inspect 'Instances found: [A, B]' and remove one of the duplicated backend-scoped beans.
  2. Narrow each bean's scope with an index-level qualifier (@SearchExtension(backend = ..., index = ...)) if both are intentionally index-specific, so backend resolution sees one bean.
  3. Mark your override with @Alternative and @Priority so it replaces the @DefaultBean instead of coexisting with it.
  4. Verify only one @SearchExtension qualifier matches by checking the backend name spelling in the annotations.

Example fix

// before: two backend-wide beans of the same type
@SearchExtension(backend = "elasticsearch")
public class MyConfigA implements ElasticsearchConnectionConfigurer { ... }
@SearchExtension(backend = "elasticsearch")
public class MyConfigB implements ElasticsearchConnectionConfigurer { ... }

// after: single backend bean (B removed or repurposed to an index scope)
@SearchExtension(backend = "elasticsearch")
public class MyConfigA implements ElasticsearchConnectionConfigurer { ... }
@SearchExtension(backend = "elasticsearch", index = "Books")
public class MyConfigB implements ... { ... }
Defensive patterns

Strategy: validation

Validate before calling

// Verify no duplicate backend-scoped extension beans of the same type:
long count = Arc.container().instance(MyBackendCustomizer.class)
    .handlesStream().count();
if (count > 1) {
    throw new IllegalStateException("Expected 1 backend customizer, found " + count);
}

Try / catch

try {
    startHibernateSearch();
} catch (IllegalStateException e) {
    if (e.getMessage() != null && e.getMessage().contains("Hibernate Search backend")) {
        LOGGER.error("Keep exactly one bean per backend: " + e.getMessage());
    }
    throw e;
}

Prevention

When it happens

Trigger: singleExtensionBeanReferenceFor invoked with indexName == null and backendName != null; extensionInstanceFor(...) selects beans qualified with @SearchExtension(backend = "<name>") (no index qualifier), and the resulting InjectableInstance is ambiguous (isAmbiguous() true).

Common situations: Two @SearchExtension(backend = "elasticsearch")-annotated beans of the same type (e.g. two DocumentReferenceProviders, two mass indexers customizations, two connection/tenant configurations) without an index qualifier; a user override bean plus the extension's @DefaultBean both active because the override lacks @Alternative/@Priority; duplicate registrations after upgrading Hibernate Search where an old customizer bean remains.

Related errors


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