quarkusio/quarkus · error · OIDCException

Tenant configuration has not been resolved

Error message

Tenant configuration has not been resolved

What it means

OidcAuthenticationMechanism.apply() resolves the tenant configuration for the incoming request. If resolver.resolveConfig() completes with a null OidcTenantConfig, OIDCException 'Tenant configuration has not been resolved' is thrown because authentication cannot proceed without a tenant.

Source

Thrown at extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcAuthenticationMechanism.java:91

                return isWebApp(context, oidcTenantConfig) ? codeAuth.getChallenge(context)
                        : bearerAuth.getChallenge(context);
            }
        });
    }

    private Uni<OidcTenantConfig> resolve(RoutingContext context) {
        OidcTenantConfig resolvedConfig = context.get(OidcTenantConfig.class.getName());
        if (resolvedConfig != null) {
            return Uni.createFrom().item(resolvedConfig);
        }

        setTenantIdAttribute(context);

        return resolver.resolveConfig(context).map(new Function<>() {
            @Override
            public OidcTenantConfig apply(OidcTenantConfig oidcTenantConfig) {
                if (oidcTenantConfig == null) {
                    throw new OIDCException("Tenant configuration has not been resolved");
                }
                final String tenantId = oidcTenantConfig.tenantId().orElse(OidcUtils.DEFAULT_TENANT_ID);
                LOG.debugf("Resolved OIDC tenant id: %s", tenantId);
                context.put(OidcTenantConfig.class.getName(), oidcTenantConfig);
                if (context.get(OidcUtils.TENANT_ID_ATTRIBUTE) == null) {
                    context.put(OidcUtils.TENANT_ID_ATTRIBUTE, tenantId);
                }
                return oidcTenantConfig;
            };
        });
    }

    private boolean isWebApp(RoutingContext context, OidcTenantConfig oidcConfig) {
        ApplicationType applicationType = oidcConfig.applicationType().orElse(ApplicationType.SERVICE);
        if (ApplicationType.HYBRID == applicationType) {
            return context.request().getHeader("Authorization") == null;
        }
        return ApplicationType.WEB_APP == applicationType;

View on GitHub (pinned to e1c734241f)

Solutions

  1. In your TenantConfigResolver, return a valid OidcTenantConfig or fall back to the default tenant instead of emitting null.
  2. If the tenant genuinely cannot be authenticated for this request, return Uni.createFrom().failure(new AuthenticationFailedException(...)) so OIDC treats it as an auth failure rather than an internal error.
  3. Check resolver matching logic (host header, path prefix, query parameter) against the actual incoming request and add a catch-all mapping.

Example fix

// before
public Uni<OidcTenantConfig> resolve(RoutingContext ctx) {
    return tenants.containsKey(name(ctx)) ? Uni.createFrom().item(tenants.get(name(ctx))) : Uni.createFrom().item((OidcTenantConfig) null);
}

// after
public Uni<OidcTenantConfig> resolve(RoutingContext ctx) {
    OidcTenantConfig cfg = tenants.get(name(ctx));
    return cfg != null ? Uni.createFrom().item(cfg)
        : Uni.createFrom().failure(new AuthenticationFailedException("Unknown tenant"));
}
Defensive patterns

Strategy: validation

Validate before calling

public Uni<OidcTenantConfig> resolve(RoutingContext ctx) {
    OidcTenantConfig cfg = lookup(ctx);
    if (cfg == null) {
        return Uni.createFrom().failure(new AuthenticationFailedException("Unknown tenant"));
    }
    return Uni.createFrom().item(cfg);
}

Type guard

boolean isResolved(OidcTenantConfig cfg) {
    return cfg != null && cfg.tenantId().isPresent();
}

Try / catch

try {
    await().atMost(5, SECONDS).until(() -> resolver.resolve(ctx).await().indefinitely() != null);
} catch (Exception e) {
    // fall back to default tenant or reject request
}

Prevention

When it happens

Trigger: A custom TenantConfigResolver returns Uni.createFrom().item(null) (or null item) for a request; dynamic tenant resolution fails to match the request (path/host/header) and the resolver still emits null instead of failing or falling back to the default tenant.

Common situations: Multi-tenant apps where the resolver's matching logic doesn't cover all hosts/paths; requests hitting an unmapped tenant at boot before dynamic tenants are registered; typos in tenant identifiers within the resolver.

Related errors


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