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

  1. Verify the tokenEndpointUri configuration points at the real token endpoint (usually ends in /token, not /authorize or a login page).
  2. 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.
  3. If behind a corporate proxy, configure proxy settings so the request reaches the OAuth provider directly.
  4. 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

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


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)