{"record":{"id":"abfda02a24b76178","repo":"karatelabs/karate","slug":"token-exchange-failed-e-getmessage","errorCode":null,"errorMessage":"Token exchange failed: \" + e.getMessage()","messagePattern":"Token exchange failed: \" \\+ e\\.getMessage\\(\\)","errorType":"exception","errorClass":"OAuth2Exception","httpStatus":null,"severity":"error","filePath":"karate-core/src/main/java/io/karatelabs/http/AuthorizationCodeAuthHandler.java","lineNumber":217,"sourceCode":"            } 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) {\n        int[] ports = getConfiguredPorts();\n\n        Exception lastException = null;\n        for (int port : ports) {\n            try {\n                String redirectUri = server.start(port);\n                logger.debug(\"Callback server started on port {}\", port);\n                return redirectUri;","sourceCodeStart":199,"sourceCodeEnd":235,"githubUrl":"https://github.com/karatelabs/karate/blob/a22eb90246d958d15a47bf436693d0121ad2812d/karate-core/src/main/java/io/karatelabs/http/AuthorizationCodeAuthHandler.java#L199-L235","documentation":"Wrapper thrown by exchangeCodeForToken's catch (OAuth2Exception e) block: any OAuth2Exception raised while exchanging the code (invalid response, provider error, etc.) is re-thrown as a new OAuth2Exception with the 'Token exchange failed:' prefix, chained to the original cause.","triggerScenarios":"Any OAuth2Exception thrown inside exchangeCodeForToken — invalid/unexpected token response, provider error field, or lower-level OAuth2 failures — gets re-wrapped with this message.","commonSituations":"The same situations as errors 280-282, observed at the outer boundary; logs will show 'Token exchange failed: Token request failed: invalid_grant' style chains.","solutions":["Inspect the cause chain (e.getCause()) — the real reason is the inner OAuth2Exception message.","Read the inner message: 'Token request failed: <code>' means a provider rejection; 'invalid response' means non-JSON body.","Fix the underlying condition per the inner error (credentials, redirect_uri, code reuse, endpoint health)."],"exampleFix":"// before: only printing top-level message\nlogger.error(\"auth failed: {}\", e.getMessage());\n// after: log the cause chain\nThrowable cause = e.getCause();\nlogger.error(\"auth failed: {} (cause: {})\", e.getMessage(), cause == null ? \"none\" : cause.getMessage());","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    handler.token();\n} catch (OAuth2Exception e) {\n    Throwable root = e;\n    while (root.getCause() != null) { root = root.getCause(); }\n    logger.error(\"Token exchange failed, root cause: {}\", root.getMessage());\n}","preventionTips":["Always log the full cause chain, not just the top-level message.","Treat this wrapper as a signal to look at the inner OAuth2Exception for the actionable error."],"tags":["oauth2","error-handling"],"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"}