SonarSource/sonarqube · error · IllegalArgumentException

Unable to contact Bitbucket Cloud servers: Configure the OAu

Error message

Unable to contact Bitbucket Cloud servers: Configure the OAuth client in the Bitbucket workspace to be a private client

What it means

During validateAccessToken() the client exchanges a Bitbucket OAuth client id/secret for an access token at bitbucket.org/site/oauth2/access_token. Bitbucket answered with an error body whose 'error' field is 'invalid_grant', so the client throws IllegalArgumentException('Unable to contact Bitbucket Cloud servers: Configure the OAuth client in the Bitbucket workspace to be a private client'). Bitbucket returns invalid_grant for this grant_type when the OAuth consumer is a public (non-private) client, which does not permit the client_credentials grant used here.

Source

Thrown at server/sonar-alm-client/src/main/java/org/sonar/alm/client/bitbucket/bitbucketcloud/BitbucketCloudRestClient.java:138

      doGetWithApiToken(encodedApiTokenCredentials, buildUrl("/repositories/" + workspace), r -> null);
    } catch (NotFoundException | IllegalStateException e) {
      throw new IllegalArgumentException(e.getMessage());
    }
  }

  private Token validateAccessToken(String clientId, String clientSecret) {
    Request request = createAccessTokenRequest(clientId, clientSecret);
    try (Response response = client.newCall(request).execute()) {
      if (response.isSuccessful()) {
        return buildGson().fromJson(response.body().charStream(), Token.class);
      }

      ErrorDetails errorMsg = getTokenError(response.body(), response.message());
      if (errorMsg.body != null) {
        LOG.atInfo().log(() -> String.format(BBC_FAIL_WITH_RESPONSE, response.request().url(), response.code(), errorMsg.body));
        switch (errorMsg.body) {
          case "invalid_grant":
            throw new IllegalArgumentException(UNABLE_TO_CONTACT_BBC_SERVERS + ": " + OAUTH_CONSUMER_NOT_PRIVATE);
          case "unauthorized_client":
            throw new IllegalArgumentException(UNABLE_TO_CONTACT_BBC_SERVERS + ": " + UNAUTHORIZED_CLIENT);
          default:
            if (errorMsg.parsedErrorMsg != null) {
              throw new IllegalArgumentException(ERROR_BBC_SERVERS + ": " + errorMsg.parsedErrorMsg);
            } else {
              throw new IllegalArgumentException(UNABLE_TO_CONTACT_BBC_SERVERS);
            }
        }
      } else {
        LOG.atInfo().log(() -> String.format(BBC_FAIL_WITH_RESPONSE, response.request().url(), response.code(), response.message()));
      }
      throw new IllegalArgumentException(UNABLE_TO_CONTACT_BBC_SERVERS);

    } catch (IOException e) {
      LOG.info(String.format(BBC_FAIL_WITH_ERROR, request.url(), e.getMessage()));
      throw new IllegalArgumentException(UNABLE_TO_CONTACT_BBC_SERVERS, e);
    }

View on GitHub (pinned to 184c821202)

Solutions

  1. In Bitbucket: Workspace Settings > OAuth consumers, edit the consumer used by SonarQube and enable 'This is a private consumer' (callback URL not required for client_credentials)
  2. Re-create the consumer as private if it cannot be edited, then copy the new Key/Secret into SonarQube's Bitbucket Cloud configuration
  3. Ensure the consumer has the Account: Read, Repository: Read and Pull Request: Read scopes so the subsequent pullrequest scope check passes
  4. Retry the SonarQube configuration validation ('Check configuration') after saving the consumer changes

Example fix

// before (public consumer -> invalid_grant)
POST https://bitbucket.org/site/oauth2/access_token
  grant_type=client_credentials, Basic <clientId>:<clientSecret>  => 400 {"error":"invalid_grant"}
// after (consumer marked private in Bitbucket workspace settings)
// same request => 200 {"access_token":"...","scopes":"...pullrequest..."}
Defensive patterns

Strategy: validation

Validate before calling

// Pre-check before wiring SonarQube: confirm consumer is private by attempting the grant manually
// curl -s -X POST -u <key>:<secret> -d grant_type=client_credentials \
//   https://bitbucket.org/site/oauth2/access_token
// If body contains "error":"invalid_grant" -> consumer is public or misconfigured; fix in Bitbucket first.
if (tokenResponse.contains("\"invalid_grant\"")) {
  throw new ConfigurationException("Mark the Bitbucket OAuth consumer as private (workspace settings) before validating");
}

Try / catch

try {
  client.validate(clientId, clientSecret, workspace);
} catch (IllegalArgumentException e) {
  if (e.getMessage().contains("private client")) {
    log.error("Bitbucket OAuth consumer is public; enable 'This is a private consumer' in workspace settings");
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling BitbucketCloudRestClient.validate(clientId, clientSecret, workspace) (or any flow reaching validateAccessToken/token) where the OAuth consumer configured in the Bitbucket workspace is created as a public client: the POST to /site/oauth2/access_token with grant_type=client_credentials and Basic clientId:clientSecret returns a 400 body {"error":"invalid_grant", ...} and the switch in validateAccessToken maps it to this exact message.

Common situations: Admin follows older integration docs and creates the OAuth consumer without checking 'This is a private consumer'; consumer was later switched to public in Bitbucket workspace settings; wrong consumer selected (a public app-level consumer instead of the workspace private consumer); consumer lacking required scopes such as repository/pullrequest read.

Related errors


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