{"record":{"id":"796d9c36a8b9255e","repo":"quarkusio/quarkus","slug":"token-exchange-is-required-but-oidc-client-is-conf-796d9c","errorCode":null,"errorMessage":"Token exchange is required but OIDC client is configured to use the <grantType> grantType","messagePattern":"Token exchange is required but OIDC client is configured to use the <grantType> grantType","errorType":"validation","errorClass":"ConfigurationException","httpStatus":null,"severity":"error","filePath":"extensions/oidc-token-propagation-reactive/runtime/src/main/java/io/quarkus/oidc/token/propagation/reactive/AccessTokenRequestReactiveFilter.java","lineNumber":71,"sourceCode":"    }\n\n    @PostConstruct\n    public void initExchangeTokenClient() {\n        if (isExchangeToken()) {\n            OidcClients clients = Arc.container().instance(OidcClients.class).get();\n            String clientName = getClientName();\n            exchangeTokenClient = clientName != null ? clients.getClient(clientName) : clients.getClient();\n            Grant.Type exchangeTokenGrantType = ConfigProvider.getConfig()\n                    .getValue(\n                            \"quarkus.oidc-client.\" + (clientName != null ? clientName + \".\" : \"\")\n                                    + \"grant.type\",\n                            Grant.Type.class);\n            if (exchangeTokenGrantType == Grant.Type.EXCHANGE) {\n                exchangeTokenProperty = OidcConstants.EXCHANGE_GRANT_SUBJECT_TOKEN;\n            } else if (exchangeTokenGrantType == Grant.Type.JWT) {\n                exchangeTokenProperty = OidcConstants.JWT_BEARER_GRANT_ASSERTION;\n            } else {\n                throw new ConfigurationException(\"Token exchange is required but OIDC client is configured \"\n                        + \"to use the \" + exchangeTokenGrantType.getGrantType() + \" grantType\");\n            }\n        }\n    }\n\n    protected boolean isExchangeToken() {\n        return ConfigProvider.getConfig()\n                .getValue(\"quarkus.rest-client-oidc-token-propagation.exchange-token\", boolean.class);\n    }\n\n    @Override\n    public void filter(ResteasyReactiveClientRequestContext requestContext) {\n        if (skipPropagation(requestContext)) {\n            return;\n        }\n\n        if (verifyTokenInstance(requestContext)) {\n            if (exchangeTokenClient != null) {","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/oidc-token-propagation-reactive/runtime/src/main/java/io/quarkus/oidc/token/propagation/reactive/AccessTokenRequestReactiveFilter.java#L53-L89","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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)"],"exampleFix":"// before\nquarkus.oidc-client.exchange-client.auth-server-url=...\nquarkus.oidc-client.exchange-client.grant.type=client\n// after\nquarkus.oidc-client.exchange-client.grant.type=exchange\nquarkus.oidc-client.exchange-client.grant.exchange.subject-token=...","handlingStrategy":"validation","validationCode":"OidcClientConfig cfg = ...; // resolve named client config\nString grantType = cfg.grant().type().name().toLowerCase();\nif (!grantType.equals(\"exchange\") && !grantType.equals(\"jwt\")) {\n    throw new IllegalStateException(\"Exchange token propagation requires grant.type=exchange (or jwt), got: \" + grantType);\n}","typeGuard":null,"tryCatchPattern":"try {\n    restClient.callApi();\n} catch (ConfigurationException e) {\n    if (e.getMessage().contains(\"grantType\")) {\n        throw new IllegalStateException(\"Set quarkus.oidc-client.<name>.grant.type=exchange for token exchange\", e);\n    }\n    throw e;\n}","preventionTips":["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"],"tags":["quarkus","oidc","configuration","oauth2","rest-client"],"backgroundTag":"invalid-oauth2-grant-type","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"}