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
- Check the reported status code: 404 means the token endpoint URL is wrong — fix issuerUrl/discovery; 429 means back off and retry later.
- 5xx/502/503: retry with backoff; check IdP health/status page.
- Inspect IdP server logs for the request to see why it rejected it.
- 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
- Verify issuerUrl so discovery yields the correct token_endpoint path.
- Implement backoff for transient 429/5xx instead of tight re-auth loops.
- Check IdP/gateway status pages during incidents.
- Inspect IdP server logs for rejected requests to see the reason.
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
- Unable to obtain an access token: ${e.getMessage}
- Cannot obtain authorization metadata from ${metadataUrl}
- HTTP response body exceeds the configured maximum of ${confi
- earlyTokenRefreshPercent must be greater than 0.
- Required configuration parameters: tlsCertFile, tlsKeyFile
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/f001ab491e26c86a.
Report an issue: GitHub.