{"record":{"id":"20386edcb4e675a6","repo":"quarkusio/quarkus","slug":"multiple-io-quarkus-oidc-tenantresolver-beans-regi","errorCode":null,"errorMessage":"Multiple io.quarkus.oidc.TenantResolver beans registered","messagePattern":"Multiple io\\.quarkus\\.oidc\\.TenantResolver beans registered","errorType":"validation","errorClass":"java.lang.IllegalStateException","httpStatus":null,"severity":"error","filePath":"extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/StaticTenantResolver.java","lineNumber":47,"sourceCode":"\nfinal class StaticTenantResolver {\n\n    private static final Logger LOG = Logger.getLogger(StaticTenantResolver.class);\n\n    private final TenantResolver[] staticTenantResolversGroup1;\n    private final IssuerBasedTenantResolver issuerBasedTenantResolver;\n    private final TenantResolver[] staticTenantResolversGroup2;\n\n    StaticTenantResolver(TenantConfigBean tenantConfigBean, String rootPath, boolean resolveTenantsWithIssuer,\n            Instance<TenantResolver> tenantResolverInstance) {\n        List<TenantResolver> staticTenantResolvers = new ArrayList<>();\n        // STATIC TENANT RESOLVERS BY PRIORITY:\n        // 0. annotation based resolver\n\n        // 1. custom tenant resolver\n        if (tenantResolverInstance.isResolvable()) {\n            if (tenantResolverInstance.isAmbiguous()) {\n                throw new IllegalStateException(\"Multiple \" + TenantResolver.class + \" beans registered\");\n            }\n            staticTenantResolvers.add(tenantResolverInstance.get());\n        }\n\n        // 2. path-matching tenant resolver\n        var pathMatchingTenantResolver = PathMatchingTenantResolver.of(tenantConfigBean.getStaticTenantsConfig(), rootPath,\n                tenantConfigBean.getDefaultTenant());\n        if (pathMatchingTenantResolver != null) {\n            staticTenantResolvers.add(pathMatchingTenantResolver);\n        }\n\n        this.staticTenantResolversGroup1 = staticTenantResolvers.toArray(new TenantResolver[0]);\n\n        // 3. issuer-based tenant resolver\n        if (resolveTenantsWithIssuer) {\n            this.issuerBasedTenantResolver = IssuerBasedTenantResolver.of(\n                    tenantConfigBean.getStaticTenantsConfig(), tenantConfigBean.getDefaultTenant());\n        } else {","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/StaticTenantResolver.java#L29-L65","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Remove one of the duplicate TenantResolver bean implementations","Make only one resolver active using @Alternative with @Priority, or @IfBuildProfile/@LookupIfProperty conditions","Merge the resolution logic of both resolvers into a single TenantResolver"],"exampleFix":"// before: two @ApplicationScoped TenantResolver beans\n// after: single bean merging logic, or disable one\n@ApplicationScoped\n@LookupIfProperty(name = \"tenant.resolver.v2.enabled\", stringValue = \"true\")\npublic class V2TenantResolver implements TenantResolver { ... }","handlingStrategy":"validation","validationCode":"Instance<TenantResolver> resolvers = cdi.select(TenantResolver.class);\nif (resolvers.stream().count() > 1) throw new IllegalStateException(\"Register only one TenantResolver bean\");","typeGuard":null,"tryCatchPattern":"try { buildResolver(); } catch (IllegalStateException e) { log.error(\"Multiple TenantResolver beans: keep one (use @Alternative/@Priority for the active one)\", e); }","preventionTips":["Search the classpath for TenantResolver implementations before adding a new one","Gate resolvers with @LookupIfProperty/@IfBuildProfile so only one activates","Merge multi-tenant routing logic into a single resolver"],"tags":["oidc","cdi","tenant-resolver","ambiguous-beans"],"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-12T22:17:10.623Z"}