quarkusio/quarkus · error · java.lang.IllegalStateException

Multiple io.quarkus.oidc.TenantResolver beans registered

Error message

Multiple io.quarkus.oidc.TenantResolver beans registered

What it means

StaticTenantResolver's constructor throws IllegalStateException when the CDI instance of io.quarkus.oidc.TenantResolver is ambiguous, i.e. more than one custom TenantResolver bean is registered. Quarkus supports at most one custom tenant resolver; multiple beans make tenant selection non-deterministic.

Source

Thrown at extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/StaticTenantResolver.java:47

final class StaticTenantResolver {

    private static final Logger LOG = Logger.getLogger(StaticTenantResolver.class);

    private final TenantResolver[] staticTenantResolversGroup1;
    private final IssuerBasedTenantResolver issuerBasedTenantResolver;
    private final TenantResolver[] staticTenantResolversGroup2;

    StaticTenantResolver(TenantConfigBean tenantConfigBean, String rootPath, boolean resolveTenantsWithIssuer,
            Instance<TenantResolver> tenantResolverInstance) {
        List<TenantResolver> staticTenantResolvers = new ArrayList<>();
        // STATIC TENANT RESOLVERS BY PRIORITY:
        // 0. annotation based resolver

        // 1. custom tenant resolver
        if (tenantResolverInstance.isResolvable()) {
            if (tenantResolverInstance.isAmbiguous()) {
                throw new IllegalStateException("Multiple " + TenantResolver.class + " beans registered");
            }
            staticTenantResolvers.add(tenantResolverInstance.get());
        }

        // 2. path-matching tenant resolver
        var pathMatchingTenantResolver = PathMatchingTenantResolver.of(tenantConfigBean.getStaticTenantsConfig(), rootPath,
                tenantConfigBean.getDefaultTenant());
        if (pathMatchingTenantResolver != null) {
            staticTenantResolvers.add(pathMatchingTenantResolver);
        }

        this.staticTenantResolversGroup1 = staticTenantResolvers.toArray(new TenantResolver[0]);

        // 3. issuer-based tenant resolver
        if (resolveTenantsWithIssuer) {
            this.issuerBasedTenantResolver = IssuerBasedTenantResolver.of(
                    tenantConfigBean.getStaticTenantsConfig(), tenantConfigBean.getDefaultTenant());
        } else {

View on GitHub (pinned to e1c734241f)

Solutions

  1. Remove one of the duplicate TenantResolver bean implementations
  2. Make only one resolver active using @Alternative with @Priority, or @IfBuildProfile/@LookupIfProperty conditions
  3. Merge the resolution logic of both resolvers into a single TenantResolver

Example fix

// before: two @ApplicationScoped TenantResolver beans
// after: single bean merging logic, or disable one
@ApplicationScoped
@LookupIfProperty(name = "tenant.resolver.v2.enabled", stringValue = "true")
public class V2TenantResolver implements TenantResolver { ... }
Defensive patterns

Strategy: validation

Validate before calling

Instance<TenantResolver> resolvers = cdi.select(TenantResolver.class);
if (resolvers.stream().count() > 1) throw new IllegalStateException("Register only one TenantResolver bean");

Try / catch

try { buildResolver(); } catch (IllegalStateException e) { log.error("Multiple TenantResolver beans: keep one (use @Alternative/@Priority for the active one)", e); }

Prevention

When it happens

Trigger: Application (or a library) registers two or more beans implementing io.quarkus.oidc.TenantResolver; the OIDC runtime builds StaticTenantResolver during tenant resolution, sees tenantResolverInstance.isAmbiguous(), and throws.

Common situations: Adding a second TenantResolver (e.g. a library dependency already provides one); copy-pasting a resolver class in a multi-module app; forgetting @Alternative/@Priority exclusions leading to duplicate beans.

Related errors


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