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
- Set quarkus.oidc-client.<name>.grant.type=exchange for the client used by token propagation
- Use grant.type=jwt if you are sending a JWT assertion as the subject token instead of RFC 8693 token exchange
- 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
- Always set quarkus.oidc-client.<name>.grant.type=exchange on the client used for token exchange propagation
- Do not share one OIDC client config between plain client-credentials flows and exchange flows
- Review grant type after upgrading Quarkus, since defaults can shift
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
- The '%s' property can only be set to 'idtoken' for WEB_APP a
- '%s' must be enabled to use '%s'
- UserInfo is required but DefaultTokenStateManager is configu
- Access token is required to check the roles but DefaultToken
- UserInfo path is missing but 'verifyAccessTokenWithUserInfo'
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/796d9c36a8b9255e.
Report an issue: GitHub.