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
- 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)
- Re-create the consumer as private if it cannot be edited, then copy the new Key/Secret into SonarQube's Bitbucket Cloud configuration
- Ensure the consumer has the Account: Read, Repository: Read and Pull Request: Read scopes so the subsequent pullrequest scope check passes
- 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
- Always create Bitbucket OAuth consumers with 'This is a private consumer' checked — required for grant_type=client_credentials
- Use a dedicated workspace-level consumer for SonarQube, not a personal/public one
- Grant the consumer Account:Read, Repository:Read and Pull Request:Read scopes
- Re-run 'Check configuration' after any consumer edit in Bitbucket
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
- Unable to contact Bitbucket Cloud servers: Check your creden
- Can not get Bitbucket user profile. HTTP code: %s, response:
- Error returned by Bitbucket Cloud: The OAuth client in the B
- Error returned by Bitbucket Cloud: %s
- Unable to contact Bitbucket Cloud servers
AI-assisted analysis of SonarSource/sonarqube@184c821202 (2026-09-09).
Data as JSON: /api/errors/4c603267330e16cb.
Report an issue: GitHub.