karatelabs/karate · error · OAuth2Exception
Token endpoint returned invalid response: " +…
Error message
Token endpoint returned invalid response: " + truncate(bodyString)
What it means
Thrown by AuthorizationCodeAuthHandler.exchangeCodeForToken when the OAuth2 token endpoint's response body cannot be parsed as JSON at all (Json.of throws). The library requires a JSON body from the token endpoint per RFC 6749, so a non-JSON body (HTML error page, empty body, plain text) is treated as a fatal OAuth2 failure.
Solutions
- Verify the tokenEndpointUri configuration points at the real token endpoint (usually ends in /token, not /authorize or a login page).
- Check the actual HTTP status and raw body returned by the endpoint (curl the token endpoint or inspect logs) — an HTML body usually means a proxy/gateway error.
- If behind a corporate proxy, configure proxy settings so the request reaches the OAuth provider directly.
- Retry after confirming the provider's token endpoint is healthy; a transient gateway failure is a common cause.
Example fix
// before
OAuth2Config cfg = OAuth2Config.builder()
.tokenEndpointUri("https://provider.com/authorize") // wrong endpoint
.build();
// after
OAuth2Config cfg = OAuth2Config.builder()
.tokenEndpointUri("https://provider.com/oauth2/token") // JSON token endpoint
.build(); Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-check: token endpoint should be reachable and speak JSON
String uri = cfg.getTokenEndpointUri();
if (uri == null || !uri.startsWith("https://") || uri.contains("authorize")) {
throw new IllegalStateException("Suspect tokenEndpointUri: " + uri);
} Try / catch
try {
handler.token();
} catch (OAuth2Exception e) {
if (e.getMessage().startsWith("Token endpoint returned invalid response")) {
logger.error("Non-JSON token response; check endpoint/proxy: {}", e.getMessage());
// inspect raw endpoint, fix tokenEndpointUri or proxy config
}
} Prevention
- Point tokenEndpointUri at the /token endpoint, never /authorize or a login page.
- Curl the token endpoint once to confirm it returns JSON.
- Configure corporate proxy settings before running OAuth flows.
- Alert on HTML/empty bodies from token endpoints in CI logs.
When it happens
Trigger: The POST to the token endpoint (via builder.invoke("post")) returned a body that is not valid JSON — e.g. an HTML gateway/proxy error page, an empty 200 response, a plain-text message, or a truncated response.
Common situations: Corporate proxy or API gateway intercepting the token request and returning an HTML error page; wrong tokenEndpointUri pointing at a login page; server returning 502/503 with a non-JSON body; network middleware stripping the body.
Understand the failure class
Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.
Related errors
- Token endpoint returned unexpected response: " +…
- client credentials auth request failed: " + e.getMessage()
- Token request failed: " + error + (desc != null ? " - " +…
- Could not start callback server on any configured port: " +…
- missing argument for paramJson()
AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12).
Data as JSON: /api/errors/3456ce10d9525cf5.
Report an issue: GitHub.
Appendix: source
Thrown at karate-core/src/main/java/io/karatelabs/http/AuthorizationCodeAuthHandler.java:200
builder.formField("redirect_uri", redirectUri);
builder.formField("client_id", config.get("client_id"));
builder.formField("code_verifier", codeVerifier);
// Optional client_secret (for confidential clients)
if (config.containsKey("client_secret")) {
builder.formField("client_secret", config.get("client_secret"));
}
builder.header("Accept", "application/json");
try {
HttpResponse response = builder.invoke("post");
String bodyString = response.getBodyString();
Json json;
try {
json = Json.of(bodyString);
} 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) {View on GitHub (pinned to a22eb90246)