apache/pulsar · error · PulsarClientException.AuthenticationException

Unable to obtain an access token: ${e.getMessage}

Error message

Unable to obtain an access token: ${e.getMessage}

What it means

TlsClientAuthFlow.authenticate sends a token request to the OAuth2 token endpoint. If the HTTP exchange throws IOException or the IdP returns an RFC 6749 token error surfaced as TokenExchangeException, a PulsarClientException.AuthenticationException is thrown with 'Unable to obtain an access token: <reason>'. Authentication with the authorization server failed.

Source

Thrown at pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/TlsClientAuthFlow.java:142

        initialized = true;
    }

    public TokenResult authenticate() throws PulsarClientException {
        // request an access token using TLS client authentication
        ClientCredentialsExchangeRequest req = ClientCredentialsExchangeRequest.builder()
                .clientId(this.clientId)
                .audience(this.audience)
                .scope(this.scope)
                .authMethod(TokenEndpointAuthMethod.TLS_CLIENT_AUTH)
                .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();
        }
    }

    @VisibleForTesting
    String getClientId() {
        return clientId;
    }

View on GitHub (pinned to 820761864e)

Solutions

  1. Inspect the embedded message: a TokenError code like invalid_client means fix clientId/clientSecret/audience; an IO message means check connectivity to the token endpoint.
  2. Verify network reachability of the issuer/token endpoint (curl it; check proxy settings).
  3. Confirm issuerUrl points to the correct tenant/realm — token endpoint may have changed.
  4. Re-issue rotated credentials and update the client config or credentials file.

Example fix

// before
String secret = System.getenv("CLIENT_SECRET"); // stale rotated value -> 401 invalid_client
// after
String secret = loadFromSecretStore("pulsar-client-secret"); // current credential
Defensive patterns

Strategy: try-catch

Try / catch

try {
    client = AuthenticationFactoryOAuth2.clientCredentials(issuerUrl, credFile, audience);
} catch (org.apache.pulsar.client.api.PulsarClientException.AuthenticationException e) {
    // message = 'Unable to obtain an access token: <reason>'
    // TokenError codes (invalid_client, invalid_grant) -> fix credentials/audience
    // IO reasons -> check network/proxy/TLS to token endpoint
    throw new RuntimeException("OAuth2 token acquisition failed: " + e.getMessage(), e);
}

Prevention

When it happens

Trigger: authenticate() runs during Pulsar client OAuth2 login and exchanger.exchangeClientCredentials(req) either fails at the network/TLS layer (IOException) or the token endpoint responds 400/401 with a TokenError (invalid_client, invalid_grant, etc.).

Common situations: Wrong or rotated clientId/clientSecret; revoked credentials; misconfigured audience; IdP downtime; corporate proxy/firewall blocking the token endpoint; TLS trust issues between client and IdP.

Related errors


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