{"record":{"id":"82acba25fe410b7a","repo":"quarkusio/quarkus","slug":"multiple-interface-io-quarkus-oidc-tenantconfigres","errorCode":null,"errorMessage":"Multiple interface io.quarkus.oidc.TenantConfigResolver beans registered","messagePattern":"Multiple interface io\\.quarkus\\.oidc\\.TenantConfigResolver beans registered","errorType":"console","errorClass":"IllegalStateException","httpStatus":null,"severity":"critical","filePath":"extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/DefaultTenantConfigResolver.java","lineNumber":98,"sourceCode":"    DefaultTenantConfigResolver(BlockingSecurityExecutor blockingExecutor, BeanManager beanManager,\n            Instance<TenantResolver> tenantResolverInstance,\n            @ConfigProperty(name = \"quarkus.oidc.resolve-tenants-with-issuer\") boolean resolveTenantsWithIssuer,\n            @ConfigProperty(name = \"quarkus.security.events.enabled\") boolean securityEventsEnabled,\n            @ConfigProperty(name = \"quarkus.http.root-path\") String rootPath, TenantConfigBean tenantConfigBean) {\n        this.blockingRequestContext = new BlockingTaskRunner<OidcTenantConfig>(blockingExecutor);\n        this.securityEventObserved = SecurityEventHelper.isEventObserved(new SecurityEvent(null, (SecurityIdentity) null),\n                beanManager, securityEventsEnabled);\n        this.tenantConfigBean = tenantConfigBean;\n        this.annotationBasedTenantResolutionEnabled = Boolean.getBoolean(OidcUtils.ANNOTATION_BASED_TENANT_RESOLUTION_ENABLED);\n        this.rootPath = rootPath;\n        this.staticTenantResolver = new StaticTenantResolver(tenantConfigBean, rootPath, resolveTenantsWithIssuer,\n                tenantResolverInstance);\n    }\n\n    @PostConstruct\n    public void verifyResolvers() {\n        if (tenantConfigResolver.isResolvable() && tenantConfigResolver.isAmbiguous()) {\n            throw new IllegalStateException(\"Multiple \" + TenantConfigResolver.class + \" beans registered\");\n        }\n        if (tokenStateManager.isAmbiguous()) {\n            throw new IllegalStateException(\"Multiple \" + TokenStateManager.class + \" beans registered\");\n        }\n        if (tokenIntrospectionCache.isAmbiguous()) {\n            throw new IllegalStateException(\"Multiple \" + TokenIntrospectionCache.class + \" beans registered\");\n        }\n        if (userInfoCache.isAmbiguous()) {\n            throw new IllegalStateException(\"Multiple \" + UserInfo.class + \" beans registered\");\n        }\n        if (javaScriptRequestChecker.isAmbiguous()) {\n            throw new IllegalStateException(\"Multiple \" + JavaScriptRequestChecker.class + \" beans registered\");\n        }\n\n    }\n\n    List<AuthenticationCompletionAction> authenticationCompletionActions() {\n        return authenticationCompletionActions;","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/DefaultTenantConfigResolver.java#L80-L116","documentation":"DefaultTenantConfigResolver's @PostConstruct verifyResolvers() checks CDI injected Instances for ambiguity. If more than one bean implements io.quarkus.oidc.TenantConfigResolver and is resolvable, Quarkus cannot pick a single tenant resolver, so startup fails with IllegalStateException naming the interface.","triggerScenarios":"Deploy an application with two or more @ApplicationScoped beans implementing TenantConfigResolver that are both eligible for injection into DefaultTenantConfigResolver (no @Default/@Alternative disambiguation), then start the app; the PostConstruct on DefaultTenantConfigResolver throws.","commonSituations":"Adding a second custom TenantConfigResolver for multi-tenant OIDC while another one (or a library-provided one) already exists; copying example code that defines a resolver while keeping an old one; version upgrades bringing in an extra resolver bean.","solutions":["Keep only one TenantConfigResolver bean; delete or merge the duplicate implementation.","Mark one bean with @Alternative/@Priority or @Default (and the others with @Alternative) so only one is resolvable.","If you intended to support multiple tenants, use static quarkus.oidc.<tenant>.* configuration or one resolver that dispatches by RoutingContext, not multiple resolvers."],"exampleFix":"// before\nclass TenantAResolver implements TenantConfigResolver { ... }\nclass TenantBResolver implements TenantConfigResolver { ... }\n\n// after - single resolver dispatching per request\nclass AppTenantResolver implements TenantConfigResolver {\n    public Uni<OidcTenantConfig> resolve(RoutingContext ctx) {\n        return ctx.request().host().startsWith(\"b.\") ? tenantB() : tenantA();\n    }\n}","handlingStrategy":"validation","validationCode":"// Before deploying: assert exactly one TenantConfigResolver bean\nimport jakarta.enterprise.inject.Instance;\nInstance<TenantConfigResolver> resolvers = CDI.current().select(TenantConfigResolver.class);\nif (resolvers.stream().count() > 1) {\n    throw new IllegalStateException(\"Register only one TenantConfigResolver\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Search the codebase for 'implements TenantConfigResolver' before adding a new one","Use @Alternative + @Priority when overriding a resolver","Prefer one resolver dispatching per request over multiple resolver beans","Run the app once locally before release; this fails at startup, not at runtime"],"tags":["cdi","oidc","startup","ambiguous-bean"],"backgroundTag":"ambiguous-cdi-bean","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}