quarkusio/quarkus · error · ConfigurationException

Token exchange is required but OIDC client is configured to

Error message

Token exchange is required but OIDC client is configured to use the <grantType> grantType

What it means

AccessTokenRequestReactiveFilter exchanges the current access token for a new one using the configured OIDC client grant. If token exchange is required (exchangeToken is true, i.e. the client is a dedicated exchange-token client) but the OIDC client's configured grant type is not a token-exchange grant (EXCHANGE or JWT bearer), the filter cannot know which property to send the subject token in, so initExchangeTokenClient throws a ConfigurationException at filter initialization.

Source

Thrown at extensions/oidc-token-propagation-reactive/runtime/src/main/java/io/quarkus/oidc/token/propagation/reactive/AccessTokenRequestReactiveFilter.java:71

    }

    @PostConstruct
    public void initExchangeTokenClient() {
        if (isExchangeToken()) {
            OidcClients clients = Arc.container().instance(OidcClients.class).get();
            String clientName = getClientName();
            exchangeTokenClient = clientName != null ? clients.getClient(clientName) : clients.getClient();
            Grant.Type exchangeTokenGrantType = ConfigProvider.getConfig()
                    .getValue(
                            "quarkus.oidc-client." + (clientName != null ? clientName + "." : "")
                                    + "grant.type",
                            Grant.Type.class);
            if (exchangeTokenGrantType == Grant.Type.EXCHANGE) {
                exchangeTokenProperty = OidcConstants.EXCHANGE_GRANT_SUBJECT_TOKEN;
            } else if (exchangeTokenGrantType == Grant.Type.JWT) {
                exchangeTokenProperty = OidcConstants.JWT_BEARER_GRANT_ASSERTION;
            } else {
                throw new ConfigurationException("Token exchange is required but OIDC client is configured "
                        + "to use the " + exchangeTokenGrantType.getGrantType() + " grantType");
            }
        }
    }

    protected boolean isExchangeToken() {
        return ConfigProvider.getConfig()
                .getValue("quarkus.rest-client-oidc-token-propagation.exchange-token", boolean.class);
    }

    @Override
    public void filter(ResteasyReactiveClientRequestContext requestContext) {
        if (skipPropagation(requestContext)) {
            return;
        }

        if (verifyTokenInstance(requestContext)) {
            if (exchangeTokenClient != null) {

View on GitHub (pinned to e1c734241f)

Solutions

  1. Set quarkus.oidc-client.<name>.grant.type=exchange for the client used by token propagation
  2. Use grant.type=jwt if you are sending a JWT assertion as the subject token instead of RFC 8693 token exchange
  3. If you only want to forward the existing access token without exchanging it, disable exchange-token propagation (do not use the exchange-token variant of @AccessToken / exchange client config)

Example fix

// before
quarkus.oidc-client.exchange-client.auth-server-url=...
quarkus.oidc-client.exchange-client.grant.type=client
// after
quarkus.oidc-client.exchange-client.grant.type=exchange
quarkus.oidc-client.exchange-client.grant.exchange.subject-token=...
Defensive patterns

Strategy: validation

Validate before calling

OidcClientConfig cfg = ...; // resolve named client config
String grantType = cfg.grant().type().name().toLowerCase();
if (!grantType.equals("exchange") && !grantType.equals("jwt")) {
    throw new IllegalStateException("Exchange token propagation requires grant.type=exchange (or jwt), got: " + grantType);
}

Try / catch

try {
    restClient.callApi();
} catch (ConfigurationException e) {
    if (e.getMessage().contains("grantType")) {
        throw new IllegalStateException("Set quarkus.oidc-client.<name>.grant.type=exchange for token exchange", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Configuring a named OIDC client (e.g. via quarkus.oidc-client.<name>.grant.type) for token exchange with a grant type other than 'exchange' or 'jwt', then annotating a REST client with @AccessToken or enabling exchange-token propagation so AccessTokenRequestReactiveFilter.initExchangeTokenClient runs and reads Grant.Type via getConfig("grant.type", Grant.Type.class).

Common situations: Copy-pasting client config that used grant.type=client or password into an exchange scenario; forgetting that exchange propagation requires quarkus.oidc-client.<name>.grant.type=exchange; typo in grant type name falling back to a default non-exchange grant; recent upgrade where the default grant changed.

Related errors


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