apache/pulsar · critical · PulsarClientException.AuthenticationException
Unable to obtain an access token: ${message}
Error message
Unable to obtain an access token: ${message} What it means
After loading the key, authenticate() performs the OAuth2 client-credentials token exchange. TokenExchangeException or IOException from exchanger.exchangeClientCredentials is wrapped as AuthenticationException('Unable to obtain an access token: <cause message>'). This means the key was read but the token request against the identity provider failed.
Source
Thrown at pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/ClientCredentialsFlow.java:184
throw new PulsarClientException.AuthenticationException("Unable to read private key: " + e.getMessage());
}
// request an access token using client credentials
ClientCredentialsExchangeRequest req = ClientCredentialsExchangeRequest.builder()
.clientId(keyFile.getClientId())
.clientSecret(keyFile.getClientSecret())
.audience(this.audience)
.scope(this.scope)
.authMethod(TokenEndpointAuthMethod.CLIENT_SECRET_POST)
.build();
TokenResult tr;
if (!initialized) {
initialize();
}
try {
tr = this.exchanger.exchangeClientCredentials(req);
} catch (TokenExchangeException | IOException e) {
throw new PulsarClientException.AuthenticationException("Unable to obtain an access token: "
+ e.getMessage());
}
return tr;
}
@Override
public void close() throws Exception {
super.close();
if (exchanger != null) {
exchanger.close();
}
}
}
View on GitHub (pinned to 820761864e)
Solutions
- Check the cause message: fix clientId/clientSecret if invalid_client
- Verify issuerUrl is correct and the IdP is reachable from the client network
- Confirm the OAuth2 app is enabled and the token endpoint allows client_credentials grant
Example fix
// before
authParams.put("clientSecret", "stale-secret"); // rotated on IdP
// after
authParams.put("clientSecret", "<newly-rotated-secret>"); Defensive patterns
Strategy: retry
Validate before calling
// preflight: ensure IdP token endpoint is reachable
try {
HttpURLConnection c = (HttpURLConnection) new java.net.URI(issuerUrl).toURL().openConnection();
c.setConnectTimeout(5000);
c.connect();
} catch (Exception e) {
throw new IllegalStateException("IdP unreachable: " + e.getMessage());
} Try / catch
try {
flow.initialize();
} catch (PulsarClientException.AuthenticationException e) {
if (e.getMessage().startsWith("Unable to obtain an access token")) {
if (e.getMessage().contains("invalid_client")) {
refreshClientCredentials(); // rotate secret and retry
} else {
backoffAndRetry(); // transient network error
}
} else throw e;
} Prevention
- Keep clientId/clientSecret in sync with IdP rotations
- Monitor IdP availability and network path from client hosts
- Validate the issuerUrl resolves to the correct tenant/realm before deploying
When it happens
Trigger: IdP rejects credentials (invalid_client), the token endpoint URL is unreachable (DNS/TLS error), or the metadata-resolved token endpoint returned an error; network outage between client and IdP.
Common situations: Expired or rotated client secret not updated in config; wrong issuerUrl so the wrong token endpoint is used; firewall/proxy blocking the POST; clock skew or audience misconfiguration on the IdP.
Related errors
- Unable to read private key: ${message}
- Unable to retrieve OAuth 2.0 server metadata
- Cannot obtain authorization metadata from ${metadataUrl}
- Failed to perform HTTP request. res: ${res.statusCode}
- Failed to update clusters because failed to create admin cli
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/bb4f0ba89ebbae1e.
Report an issue: GitHub.