SonarSource/sonarqube · error · IllegalStateException

Unable to get a token:

Error message

Unable to get a token: 

What it means

OAuthMicrosoftRestClient.getAccessTokenFromClientCredentialsGrantFlow calls ScribeJava's client-credentials token request to Azure AD. If that call fails with IOException or ExecutionException (network failure, bad credentials, malformed response), it rethrows as IllegalStateException 'Unable to get a token: <cause>'.

Source

Thrown at server/sonar-server-common/src/main/java/org/sonar/server/oauth/OAuthMicrosoftRestClient.java:37

 */
package org.sonar.server.oauth;

import com.github.scribejava.core.builder.ServiceBuilder;
import com.github.scribejava.core.oauth.OAuth20Service;
import java.io.IOException;
import java.util.concurrent.ExecutionException;

public class OAuthMicrosoftRestClient {

  public String getAccessTokenFromClientCredentialsGrantFlow(String host, String clientId, String clientSecret, String tenant, String scope) {
    final OAuth20Service service = new ServiceBuilder(clientId)
      .apiSecret(clientSecret)
      .defaultScope(scope)
      .build(new ScribeMicrosoftOauth2Api(host, tenant));
    try {
      return service.getAccessTokenClientCredentialsGrant().getAccessToken();
    } catch (IOException | ExecutionException e) {
      throw new IllegalStateException("Unable to get a token: " + e);
    } catch (InterruptedException e) {
      Thread.currentThread().interrupt();
      throw new IllegalStateException("Interrupted while getting a token: " + e);
    }
  }
}

View on GitHub (pinned to 184c821202)

Solutions

  1. Verify client id, client secret, tenant and scope values; rotate the secret if expired.
  2. Test connectivity to the token endpoint (curl https://login.microsoftonline.com) including proxy settings.
  3. Check the nested cause in logs for Azure's OAuth error (invalid_client, unauthorized_client) and fix the app registration.
  4. Confirm the ScribeMicrosoftOauth2Api host/tenant configuration matches your Azure tenant.

Example fix

// before
new OAuthMicrosoftRestClient(wrongTenantHost, clientId, oldSecret, scope);
// after
new OAuthMicrosoftRestClient(new Host("https://login.microsoftonline.com", "<tenant-id>"), clientId, rotatedSecret, scope);
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-flight checks before exchanging the token
Objects.requireNonNull(clientId); Objects.requireNonNull(clientSecret);
try (var s = new java.net.Socket(host.url().getHost(), 443)) { /* reachable */ }

Try / catch

try { token = oauthClient.getAccessTokenFromClientCredentialsGrantFlow(); } catch (IllegalStateException e) { log.error("Microsoft token exchange failed: {}", e.getMessage(), e); throw new AuthenticationException(e.getMessage(), e); }

Prevention

When it happens

Trigger: Calling the Microsoft OAuth client-credentials flow when Azure AD returns an error (invalid client secret, wrong tenant/host), or network connectivity to login.microsoftonline.com fails.

Common situations: Expired or wrong client secret; tenant id or host misconfigured; Azure AD app not granting admin consent; proxy blocking outbound HTTPS; Azure outage.

Related errors


AI-assisted analysis of SonarSource/sonarqube@184c821202 (2026-09-09). Data as JSON: /api/errors/364fed8882438b19. Report an issue: GitHub.