karatelabs/karate · error · OAuth2Exception
Token exchange failed: " + e.getMessage()
Error message
Token exchange failed: " + e.getMessage()
What it means
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.
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).
Example fix
// before: only printing top-level message
logger.error("auth failed: {}", e.getMessage());
// after: log the cause chain
Throwable cause = e.getCause();
logger.error("auth failed: {} (cause: {})", e.getMessage(), cause == null ? "none" : cause.getMessage()); Defensive patterns
Strategy: try-catch
Try / catch
try {
handler.token();
} catch (OAuth2Exception e) {
Throwable root = e;
while (root.getCause() != null) { root = root.getCause(); }
logger.error("Token exchange failed, root cause: {}", root.getMessage());
} Prevention
- 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.
When it happens
Trigger: Any OAuth2Exception thrown inside exchangeCodeForToken — invalid/unexpected token response, provider error field, or lower-level OAuth2 failures — gets re-wrapped with this message.
Common situations: 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.
Related errors
- Authorization flow failed: " + e.getMessage()
- Missing 'authorizationUrl' in OAuth config
- Missing 'client_id' in OAuth config
- Missing 'url' (token endpoint) in OAuth config
- Token endpoint returned invalid response: " +…
AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12).
Data as JSON: /api/errors/abfda02a24b76178.
Report an issue: GitHub.
Appendix: source
Thrown at karate-core/src/main/java/io/karatelabs/http/AuthorizationCodeAuthHandler.java:217
} catch (Exception e) {
throw new OAuth2Exception("Token endpoint returned invalid response: " + truncate(bodyString));
}
if (!json.isObject()) {
throw new OAuth2Exception("Token endpoint returned unexpected response: " + truncate(bodyString));
}
Map<String, Object> data = json.asMap();
if (data.containsKey("error")) {
String error = String.valueOf(data.get("error"));
String desc = data.containsKey("error_description") ? String.valueOf(data.get("error_description")) : null;
throw new OAuth2Exception("Token request failed: " + error + (desc != null ? " - " + desc : ""));
}
logger.debug("Token exchange successful");
return OAuth2Token.fromMap(data);
} catch (OAuth2Exception e) {
logger.error("Token exchange failed: {}", e.getMessage());
throw new OAuth2Exception("Token exchange failed: " + e.getMessage(), e);
} catch (Exception e) {
logger.error("Token exchange failed: {}", e.getMessage());
throw new OAuth2Exception("Token exchange failed: " + e.getMessage(), e);
}
}
/**
* Start callback server on configured or default ports
*/
private String startCallbackServer(LocalCallbackServer server) {
int[] ports = getConfiguredPorts();
Exception lastException = null;
for (int port : ports) {
try {
String redirectUri = server.start(port);
logger.debug("Callback server started on port {}", port);
return redirectUri;View on GitHub (pinned to a22eb90246)