{"record":{"id":"54442dec7cd11565","repo":"karatelabs/karate","slug":"token-request-failed-error-desc-null-desc","errorCode":null,"errorMessage":"Token request failed: \" + error + (desc != null ? \" - \" + desc : \"\")","messagePattern":"Token request failed: \" \\+ error \\+ \\(desc != null \\? \" - \" \\+ desc : \"\"\\)","errorType":"exception","errorClass":"OAuth2Exception","httpStatus":null,"severity":"error","filePath":"karate-core/src/main/java/io/karatelabs/http/AuthorizationCodeAuthHandler.java","lineNumber":209,"sourceCode":"        builder.header(\"Accept\", \"application/json\");\n\n        try {\n            HttpResponse response = builder.invoke(\"post\");\n            String bodyString = response.getBodyString();\n            Json json;\n            try {\n                json = Json.of(bodyString);\n            } catch (Exception e) {\n                throw new OAuth2Exception(\"Token endpoint returned invalid response: \" + truncate(bodyString));\n            }\n            if (!json.isObject()) {\n                throw new OAuth2Exception(\"Token endpoint returned unexpected response: \" + truncate(bodyString));\n            }\n            Map<String, Object> data = json.asMap();\n            if (data.containsKey(\"error\")) {\n                String error = String.valueOf(data.get(\"error\"));\n                String desc = data.containsKey(\"error_description\") ? String.valueOf(data.get(\"error_description\")) : null;\n                throw new OAuth2Exception(\"Token request failed: \" + error + (desc != null ? \" - \" + desc : \"\"));\n            }\n\n            logger.debug(\"Token exchange successful\");\n            return OAuth2Token.fromMap(data);\n\n        } catch (OAuth2Exception e) {\n            logger.error(\"Token exchange failed: {}\", e.getMessage());\n            throw new OAuth2Exception(\"Token exchange failed: \" + e.getMessage(), e);\n        } catch (Exception e) {\n            logger.error(\"Token exchange failed: {}\", e.getMessage());\n            throw new OAuth2Exception(\"Token exchange failed: \" + e.getMessage(), e);\n        }\n    }\n\n    /**\n     * Start callback server on configured or default ports\n     */\n    private String startCallbackServer(LocalCallbackServer server) {","sourceCodeStart":191,"sourceCodeEnd":227,"githubUrl":"https://github.com/karatelabs/karate/blob/a22eb90246d958d15a47bf436693d0121ad2812d/karate-core/src/main/java/io/karatelabs/http/AuthorizationCodeAuthHandler.java#L191-L227","documentation":"Thrown when the token endpoint returned a well-formed JSON object containing an \"error\" field — i.e. the OAuth provider explicitly rejected the token exchange (RFC 6749 section 5.2). The message includes the provider's error code and, when present, the error_description.","triggerScenarios":"POST to the token endpoint succeeded at the HTTP/JSON level but the body contains {\"error\": ...}, e.g. invalid_grant, invalid_client, unauthorized_client, or unsupported_grant_type.","commonSituations":"Authorization code already used or expired (invalid_grant); wrong client_id/client_secret (invalid_client); redirect_uri in the token request not matching the one used in the authorization request; provider account/app misconfiguration.","solutions":["Read the error code in the message: 'invalid_grant' usually means the code expired or was already redeemed — restart the authorization flow.","Verify client_id and client_secret match the provider's registered app credentials.","Ensure the redirect_uri sent in the token exchange is byte-identical to the one used in the authorization request.","Check clock skew on the machine — large drift can invalidate codes/tokens with the provider."],"exampleFix":"// before: mismatched redirect_uri\n.redirectUri(\"http://localhost:8080/callback\") // auth request used 8081\n// after: same redirect_uri in both steps\n.redirectUri(\"http://localhost:8081/callback\")","handlingStrategy":"try-catch","validationCode":"// Before the flow: assert credentials and redirect_uri are configured and consistent\nassert cfg.getClientId() != null && cfg.getClientSecret() != null;\nassert cfg.getRedirectUri() != null && cfg.getRedirectUri().startsWith(\"http://localhost:\");","typeGuard":null,"tryCatchPattern":"try {\n    handler.token();\n} catch (OAuth2Exception e) {\n    if (e.getMessage().contains(\"invalid_grant\")) {\n        restartAuthorizationFlow(); // code expired/reused — start fresh\n    } else if (e.getMessage().contains(\"invalid_client\")) {\n        rotateCredentials();\n    }\n}","preventionTips":["Never reuse an authorization code; codes are single-use and short-lived.","Keep redirect_uri byte-identical between the authorize and token requests.","Rotate client secrets promptly and keep them synced with the provider app.","Monitor NTP/clock sync on CI machines."],"tags":["oauth2","authentication","http"],"backgroundTag":"oauth-token-exchange-failed","analyzedSha":"a22eb90246d958d15a47bf436693d0121ad2812d","analyzedAt":"2026-09-12T09:01:00.220Z","contentChangedAt":"2026-09-12T09:01:00.220Z","schemaVersion":2},"datasetVersion":"2026-09-16T19:17:19.609Z"}