quarkusio/quarkus · error · IllegalStateException
Multiple instances of %1$s were found for Hibernate Search i
Error message
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
What it means
The persistence-unit-level fallback of the same ambiguity check in HibernateSearchBeanUtil.singleExtensionBeanReferenceFor: with both indexName and backendName null, the extension requests a single bean of the given type for the whole persistence unit (e.g. an ElasticsearchMapper or mass indexer customization). When two or more matching beans exist, at most one may be assigned per persistence unit, so it throws IllegalStateException listing the candidate classes.
Source
Thrown at extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/bean/HibernateSearchBeanUtil.java:44
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))
.collect(Collectors.toList()))
.or(() -> multiExtensionBeanReferencesFor(beanType, persistenceUnitName, backendName, indexName));
}
private static <T> Optional<List<BeanReference<T>>> multiExtensionBeanReferencesFor(Class<T> beanType,View on GitHub (pinned to e1c734241f)
Solutions
- Keep only one bean of that type without backend/index qualifiers; delete the duplicate shown in the message.
- Target each bean more precisely with backend and/or index qualifiers in @SearchExtension so the persistence-unit lookup becomes unique.
- Use @Alternative + @Priority on your override instead of adding a second bean alongside the @DefaultBean.
- If both beans are legitimately needed, merge their behavior into a single bean implementing the SPI.
Example fix
// before
@SearchExtension
public class CustomizerA implements ElasticsearchMappingConfigurer { ... }
@SearchExtension
public class CustomizerB implements ElasticsearchMappingConfigurer { ... }
// after: merged into one bean
@SearchExtension
public class CustomizerA implements ElasticsearchMappingConfigurer { ... } // B deleted Defensive patterns
Strategy: validation
Validate before calling
// Ensure only one persistence-unit-wide bean of the customizer type exists:
List<String> beans = Arc.container().instance(ElasticsearchMappingConfigurer.class)
.handlesStream().map(h -> h.getBean().getBeanClass().getName()).toList();
if (beans.size() > 1) {
throw new IllegalStateException("Merge these into one bean: " + beans);
} Try / catch
try {
bootStandaloneSearch();
} catch (IllegalStateException e) {
if (e.getMessage() != null && e.getMessage().contains("per persistence unit")) {
LOGGER.error("Duplicated persistence-unit-wide @SearchExtension bean: " + e.getMessage());
}
throw e;
} Prevention
- Keep a single mapper-level customization bean; merge multiple configurers into one class.
- Never declare a bare @SearchExtension bean when a @DefaultBean of the same type already exists — override with @Alternative.
- Review new SPI implementations in code review for missing backend/index qualifiers.
When it happens
Trigger: singleExtensionBeanReferenceFor called with indexName == null and backendName == null; the InjectableInstance built from beans carrying the bare @SearchExtension qualifier (no backend/index) is ambiguous, so the final else-branch formats this message and throws.
Common situations: Declaring two persistence-unit-wide customizations of the same Hibernate Search SPI type (e.g. two mapper-level bean customizers) via @SearchExtension with no target attributes; a leftover bean from a refactor duplicating an existing one; test beans leaking into the production archive via wrong package placement.
Related errors
- Multiple instances of %1$s were found for Hibernate Search i
- Multiple instances of %1$s were found for Hibernate Search b
- Multiple instances of %1$s were found for Hibernate Search S
- Multiple instances of %1$s were found for Hibernate Search S
- Multiple instances of %1$s were found for Hibernate Search S
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/032257a7e3b29029.
Report an issue: GitHub.