apache/pulsar · error · IOException

Failed to perform HTTP request. res: ${res.statusCode}

Error message

Failed to perform HTTP request. res: ${res.statusCode}

What it means

TokenClient.exchangeClientCredentials posts the grant request to the token endpoint. On HTTP statuses other than 200 and 400/401 (which are decoded into TokenExchangeException), it throws this IOException including the response status code. The token endpoint replied with an unexpected/unhandled HTTP status, so no token could be issued.

Source

Thrown at pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/protocol/TokenClient.java:112

        try {
            HttpRequest request = HttpRequest.builder(HttpRequest.Method.POST, URI.create(tokenUrl.toString()))
                    .header("Accept", "application/json")
                    .body(new HttpRequest.Bytes(body.getBytes(StandardCharsets.UTF_8),
                            "application/x-www-form-urlencoded"))
                    .build();
            HttpResponse res = httpClient.execute(request).get();

            switch (res.statusCode()) {
                case 200:
                    return ObjectMapperFactory.getMapper().reader().readValue(res.body(), TokenResult.class);

                case 400: // Bad request
                case 401: // Unauthorized
                    throw new TokenExchangeException(
                            ObjectMapperFactory.getMapper().reader().readValue(res.body(), TokenError.class));

                default:
                    throw new IOException("Failed to perform HTTP request. res: " + res.statusCode());
            }


        } catch (InterruptedException | ExecutionException e1) {
            if (e1 instanceof InterruptedException) {
                Thread.currentThread().interrupt();
            }
            throw new IOException(e1);
        }
    }
}

View on GitHub (pinned to 820761864e)

Solutions

  1. Check the reported status code: 404 means the token endpoint URL is wrong — fix issuerUrl/discovery; 429 means back off and retry later.
  2. 5xx/502/503: retry with backoff; check IdP health/status page.
  3. Inspect IdP server logs for the request to see why it rejected it.
  4. Ensure no intermediary (proxy/gateway) is rewriting or blocking the token endpoint path.

Example fix

// before
String issuer = "https://auth.example.com/"; // token endpoint resolves to /wrong/path -> 404
// after
String issuer = "https://auth.example.com/realms/my-realm"; // discovery yields correct token_endpoint
Defensive patterns

Strategy: retry

Validate before calling

// pre-check that the token endpoint answers sanely before client use
HttpResponse<String> resp = client.send(HttpRequest.newBuilder(URI.create(tokenEndpoint)).GET().build(),
        HttpResponse.BodyHandlers.ofString());
if (resp.statusCode() >= 500) {
    throw new IOException("Token endpoint unhealthy: " + resp.statusCode());
}

Try / catch

try {
    client = AuthenticationFactoryOAuth2.clientCredentials(issuerUrl, credFile, audience);
} catch (Exception e) {
    if (e.getMessage() != null && e.getMessage().contains("Failed to perform HTTP request")) {
        // parse the trailing status code; retry with backoff only for 429/5xx,
        // fail fast for 404 (wrong token endpoint path)
        throw new RuntimeException("Token endpoint returned unexpected status; verify issuerUrl", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: exchangeClientCredentials (called by the OAuth2 flows) receives e.g. 404 (wrong token endpoint path), 403, 429 (rate limited), 500/502/503 (IdP error), or a redirect status from the authorization server.

Common situations: issuerUrl pointing at the wrong tenant so the token route 404s; IdP rate limiting bursts of re-authentication; gateway/load-balancer 502/503 during IdP maintenance; reverse proxy intercepting the request with an HTML error page.

Related errors


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/f001ab491e26c86a. Report an issue: GitHub.