quarkusio/quarkus · critical · IllegalStateException
Multiple interface io.quarkus.oidc.runtime.TokenStateManager
Error message
Multiple interface io.quarkus.oidc.runtime.TokenStateManager beans registered
What it means
verifyResolvers() in DefaultTenantConfigResolver fails startup when more than one TokenStateManager bean is registered. Quarkus OIDC injects a single TokenStateManager to persist tokens (in cookies/session) and cannot disambiguate between multiple implementations.
Source
Thrown at extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/DefaultTenantConfigResolver.java:101
@ConfigProperty(name = "quarkus.security.events.enabled") boolean securityEventsEnabled,
@ConfigProperty(name = "quarkus.http.root-path") String rootPath, TenantConfigBean tenantConfigBean) {
this.blockingRequestContext = new BlockingTaskRunner<OidcTenantConfig>(blockingExecutor);
this.securityEventObserved = SecurityEventHelper.isEventObserved(new SecurityEvent(null, (SecurityIdentity) null),
beanManager, securityEventsEnabled);
this.tenantConfigBean = tenantConfigBean;
this.annotationBasedTenantResolutionEnabled = Boolean.getBoolean(OidcUtils.ANNOTATION_BASED_TENANT_RESOLUTION_ENABLED);
this.rootPath = rootPath;
this.staticTenantResolver = new StaticTenantResolver(tenantConfigBean, rootPath, resolveTenantsWithIssuer,
tenantResolverInstance);
}
@PostConstruct
public void verifyResolvers() {
if (tenantConfigResolver.isResolvable() && tenantConfigResolver.isAmbiguous()) {
throw new IllegalStateException("Multiple " + TenantConfigResolver.class + " beans registered");
}
if (tokenStateManager.isAmbiguous()) {
throw new IllegalStateException("Multiple " + TokenStateManager.class + " beans registered");
}
if (tokenIntrospectionCache.isAmbiguous()) {
throw new IllegalStateException("Multiple " + TokenIntrospectionCache.class + " beans registered");
}
if (userInfoCache.isAmbiguous()) {
throw new IllegalStateException("Multiple " + UserInfo.class + " beans registered");
}
if (javaScriptRequestChecker.isAmbiguous()) {
throw new IllegalStateException("Multiple " + JavaScriptRequestChecker.class + " beans registered");
}
}
List<AuthenticationCompletionAction> authenticationCompletionActions() {
return authenticationCompletionActions;
}
Uni<OidcTenantConfig> resolveConfig(RoutingContext context) {View on GitHub (pinned to e1c734241f)
Solutions
- Remove the duplicate TokenStateManager implementation.
- Annotate your custom implementation with @Alternative and @Priority(1) (and register it via quarkus.arc.exclude or @LookupProperties if needed) so only it is used.
- If customization is needed without replacing the bean, prefer quarkus.oidc.token-state-manager.* config options (split-tokens, encryption-required) instead of a second bean.
Example fix
// before
@ApplicationScoped
class MyTokenStateManager implements TokenStateManager { ... }
// default manager also active => ambiguous
// after
@ApplicationScoped
@Alternative
@Priority(1)
class MyTokenStateManager implements TokenStateManager { ... } Defensive patterns
Strategy: validation
Validate before calling
long managers = CDI.current().select(TokenStateManager.class).stream().count();
if (managers > 1) {
throw new IllegalStateException("Multiple TokenStateManager beans: " + managers);
} Prevention
- Always annotate replacement SPI beans with @Alternative and @Priority
- Tune quarkus.oidc.token-state-manager.* config instead of adding beans
- Audit dependencies for libraries shipping OIDC SPI implementations
- Add an ArC startup smoke test to catch ambiguity early
When it happens
Trigger: Register two beans implementing io.quarkus.oidc.runtime.TokenStateManager (custom @Alternative-less duplicates) so the injected Instance tokenStateManager.isAmbiguous() returns true at @PostConstruct time.
Common situations: Writing a custom TokenStateManager while the default one is also active (e.g. the custom bean lacks @Alternative); adding a library dependency that ships its own TokenStateManager; refactoring that leaves an old implementation annotated as a bean.
Related errors
- Multiple interface io.quarkus.oidc.TenantConfigResolver bean
- Multiple interface io.quarkus.oidc.runtime.TokenIntrospectio
- Multiple interface io.quarkus.oidc.UserInfo beans registered
- Multiple interface io.quarkus.oidc.JavaScriptRequestChecker
- Failed to find any non-blocking provider for startup actions
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/a810bf000e5f1396.
Report an issue: GitHub.