SonarSource/sonarqube · error · IllegalArgumentException

Unable to contact Bitbucket Cloud servers

Error message

Unable to contact Bitbucket Cloud servers

What it means

validateAccessToken() reached a state where it could not produce a specific OAuth error message: either the token endpoint returned a non-success status with a body whose 'error' string was unrecognized and had no parseable error_description, or the response had no usable error body at all. In both cases the client throws IllegalArgumentException('Unable to contact Bitbucket Cloud servers'). It is the generic fallback for any token-exchange failure against Bitbucket Cloud that lacks a diagnosable body, including pure transport IOExceptions.

Source

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

    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);
    }
  }

  public RepositoryList searchRepos(String encodedApiTokenCredentials, String workspace, @Nullable String repoName, Integer page, Integer pageSize) {
    String filterQuery = String.format("q=name~\"%s\"", repoName != null ? repoName : "");
    HttpUrl url = buildUrl(String.format("/repositories/%s?%s&page=%s&pagelen=%s", workspace, filterQuery, page, pageSize));
    return doGetWithApiToken(encodedApiTokenCredentials, url, r -> buildGson().fromJson(r.body().charStream(), RepositoryList.class));
  }

View on GitHub (pinned to 184c821202)

Solutions

  1. Test connectivity from the SonarQube server: curl -v https://bitbucket.org/site/oauth2/access_token; fix DNS/firewall/proxy settings (sonar.properties proxy options) accordingly
  2. Check the server INFO logs for the preceding 'Bitbucket Cloud API call to [...] failed with error: <IOException message>' or 'failed with <code> http code' lines to identify the transport cause
  3. If a corporate proxy/TLS appliance is present, install its CA into the JVM truststore or bypass it for *.bitbucket.org
  4. Check the Bitbucket Cloud status page for outages and retry the validation after incidents resolve
  5. Confirm the configured clientId/secret are non-empty and the consumer is private so the more specific branches (invalid_grant/unauthorized_client) are reachable if applicable

Example fix

// before (proxy blocks egress, IOException -> 'Unable to contact Bitbucket Cloud servers')
# sonar.properties (no proxy)
// after
sonar.proxy.host=proxy.corp.example.com
sonar.proxy.port=8080
sonar.proxy.user=svc-sonar
sonar.proxy.password=<proxy-password>
Defensive patterns

Strategy: retry

Validate before calling

// Pre-check reachability of the token endpoint before configuring
ProcessResult r = exec("curl -s -o /dev/null -w %{http_code} --max-time 10 https://bitbucket.org/site/oauth2/access_token");
if (!"000".equals(r.output()) && Integer.parseInt(r.output()) >= 500) {
  throw new EnvironmentException("bitbucket.org unreachable/unhealthy from this host; fix network before configuring");
}

Try / catch

try {
  client.validate(clientId, clientSecret, workspace);
} catch (IllegalArgumentException e) {
  if ("Unable to contact Bitbucket Cloud servers".equals(e.getMessage())) {
    // generic transport/unparsed failure: retry with backoff, then check connectivity
    retryWithBackoff(() -> client.validate(clientId, clientSecret, workspace), 3);
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling BitbucketCloudRestClient.validate(clientId, clientSecret, workspace) where: (1) the access_token POST returns non-2xx with a body having no parseable error/error_description (HTML error page, blank body, empty response) — line 145 and the trailing line-151 throw after the else branch logs; (2) an IOException occurs during the call (DNS failure, connection reset, TLS error, timeout) — line 155 throw of the same message; (3) response body is not JSON so getTokenError falls back to the raw body string with no parsedErrorMsg.

Common situations: SonarQube server cannot reach bitbucket.org (firewall/proxy/DNS misconfiguration, proxy auth required); corporate TLS interception breaking the handshake; Bitbucket returning an HTML 502/503 error page during incidents; OIDC/proxy stripping the response body; timeouts under load.

Related errors


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