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

  1. Check the cause message: fix clientId/clientSecret if invalid_client
  2. Verify issuerUrl is correct and the IdP is reachable from the client network
  3. 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

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


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