{"record":{"id":"f001ab491e26c86a","repo":"apache/pulsar","slug":"failed-to-perform-http-request-res-res-statusc","errorCode":null,"errorMessage":"Failed to perform HTTP request. res: ${res.statusCode}","messagePattern":"Failed to perform HTTP request\\. res: (.+?)","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/protocol/TokenClient.java","lineNumber":112,"sourceCode":"        try {\n            HttpRequest request = HttpRequest.builder(HttpRequest.Method.POST, URI.create(tokenUrl.toString()))\n                    .header(\"Accept\", \"application/json\")\n                    .body(new HttpRequest.Bytes(body.getBytes(StandardCharsets.UTF_8),\n                            \"application/x-www-form-urlencoded\"))\n                    .build();\n            HttpResponse res = httpClient.execute(request).get();\n\n            switch (res.statusCode()) {\n                case 200:\n                    return ObjectMapperFactory.getMapper().reader().readValue(res.body(), TokenResult.class);\n\n                case 400: // Bad request\n                case 401: // Unauthorized\n                    throw new TokenExchangeException(\n                            ObjectMapperFactory.getMapper().reader().readValue(res.body(), TokenError.class));\n\n                default:\n                    throw new IOException(\"Failed to perform HTTP request. res: \" + res.statusCode());\n            }\n\n\n        } catch (InterruptedException | ExecutionException e1) {\n            if (e1 instanceof InterruptedException) {\n                Thread.currentThread().interrupt();\n            }\n            throw new IOException(e1);\n        }\n    }\n}\n","sourceCodeStart":94,"sourceCodeEnd":124,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/protocol/TokenClient.java#L94-L124","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nString issuer = \"https://auth.example.com/\"; // token endpoint resolves to /wrong/path -> 404\n// after\nString issuer = \"https://auth.example.com/realms/my-realm\"; // discovery yields correct token_endpoint","handlingStrategy":"retry","validationCode":"// pre-check that the token endpoint answers sanely before client use\nHttpResponse<String> resp = client.send(HttpRequest.newBuilder(URI.create(tokenEndpoint)).GET().build(),\n        HttpResponse.BodyHandlers.ofString());\nif (resp.statusCode() >= 500) {\n    throw new IOException(\"Token endpoint unhealthy: \" + resp.statusCode());\n}","typeGuard":null,"tryCatchPattern":"try {\n    client = AuthenticationFactoryOAuth2.clientCredentials(issuerUrl, credFile, audience);\n} catch (Exception e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"Failed to perform HTTP request\")) {\n        // parse the trailing status code; retry with backoff only for 429/5xx,\n        // fail fast for 404 (wrong token endpoint path)\n        throw new RuntimeException(\"Token endpoint returned unexpected status; verify issuerUrl\", e);\n    }\n    throw e;\n}","preventionTips":["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."],"tags":["oauth2","http","token-endpoint","network","pulsar-client"],"backgroundTag":"unexpected-http-status","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}