SonarSource/sonarqube · error · IllegalStateException

Error returned by Bitbucket Cloud

Error message

Error returned by Bitbucket Cloud

What it means

doCall executes the HTTP request and, when the call itself throws an IOException (no HTTP response at all: DNS failure, timeout, connection reset, TLS error), logs and rethrows it as IllegalStateException("Error returned by Bitbucket Cloud", e). Despite the wording, this is not an error returned by Bitbucket — it means the request never completed and no server response was received. Successful responses with non-2xx codes are instead routed to handleError (error 24).

Source

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

  protected <G> G doGetWithApiToken(String encodedApiTokenCredentials, HttpUrl url, Function<Response, G> handler) {
    // Bitbucket Cloud expects API tokens to be transported as Basic authorization with base64(email:apiToken).
    Request request = prepareRequestWithAuthorizationHeader("Basic " + encodedApiTokenCredentials, GET, url, null);
    try {
      return doCall(request, handler);
    } catch (BitbucketCloudException e) {
      throw new IllegalStateException(e.getMessage(), e);
    }
  }

  protected <G> G doCall(Request request, Function<Response, G> handler) {
    try (Response response = client.newCall(request).execute()) {
      if (!response.isSuccessful()) {
        handleError(response);
      }
      return handler.apply(response);
    } catch (IOException e) {
      LOG.info(ERROR_BBC_SERVERS + ": {}", e.getMessage());
      throw new IllegalStateException(ERROR_BBC_SERVERS, e);
    }
  }

  private static void handleError(Response response) throws IOException {
    ErrorDetails error = getError(response.body(), response.message());
    int statusCode = response.code();
    LOG.atInfo().log(() -> String.format(BBC_FAIL_WITH_RESPONSE, response.request().url(), statusCode, error.body));
    String errorMessage;
    if (error.parsedErrorMsg != null) {
      errorMessage = ERROR_BBC_SERVERS + ": " + error.parsedErrorMsg + " [HTTP " + statusCode + "]";
    } else {
      errorMessage = UNABLE_TO_CONTACT_BBC_SERVERS + " [HTTP " + statusCode + "]";
    }
    throw new BitbucketCloudException(errorMessage, statusCode);
  }

  private static ErrorDetails getError(@Nullable ResponseBody body, @Nullable String fallbackMessage) throws IOException {
    return getErrorDetails(body, fallbackMessage, s -> {

View on GitHub (pinned to 184c821202)

Solutions

  1. Look at the IllegalStateException's cause (IOException) or the INFO log 'Error returned by Bitbucket Cloud: <message>' to identify the exact transport failure.
  2. Test connectivity from the SonarQube host: curl -v https://api.bitbucket.org/2.0/repositories/<workspace>.
  3. Configure JVM proxy settings (-Dhttps.proxyHost/-Dhttps.proxyPort/-Dhttps.nonProxyHosts) if a proxy is mandatory in your environment, then restart.
  4. If the failure is TLS-related, add the corporate CA certificate to the JVM truststore (cacerts).
  5. If it is a read timeout on large paginated responses, increase the OkHttp timeouts of the bitBucketCloudHttpClient bean and/or reduce page sizes.

Example fix

// before: no proxy configured, execute() throws IOException
Request r = new Request.Builder().url("https://api.bitbucket.org/2.0/repositories/ws").build();
// after: build the injected OkHttpClient with a proxy so execute() succeeds
OkHttpClient c = base.newBuilder()
  .proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxy.corp", 8080)))
  .connectTimeout(30, TimeUnit.SECONDS).readTimeout(60, TimeUnit.SECONDS)
  .build();
Defensive patterns

Strategy: retry

Validate before calling

// Reachability probe before first use of the client:
static boolean canReachBitbucketApi() {
  try {
    HttpURLConnection c = (HttpURLConnection) URI.create("https://api.bitbucket.org/2.0/").toURL().openConnection();
    c.setConnectTimeout(5000);
    c.setReadTimeout(10000);
    return c.getResponseCode() > 0;
  } catch (IOException e) {
    return false;
  }
}
// If false, fix DNS/proxy/firewall before calling createAccessToken/doGet/doGetWithApiToken.

Try / catch

// Since this is a transport-level failure, retry with backoff and preserve the cause:
RuntimeException last = null;
for (int attempt = 1; attempt <= 3; attempt++) {
  try {
    return doCallSomething();
  } catch (IllegalStateException e) {
    if ("Error returned by Bitbucket Cloud".equals(e.getMessage()) && e.getCause() instanceof IOException) {
      last = e;
      sleepBackoff(attempt); // e.g. 1s, 2s, 4s
      continue;
    }
    throw e; // parsed HTTP error — not retryable at transport level
  }
}
throw last;

Prevention

When it happens

Trigger: Thrown from doCall, called by createAccessToken, doGet and doGetWithApiToken, whenever client.newCall(request).execute() throws IOException while calling api.bitbucket.org or the OAuth token endpoint: unknown host, connect/read timeout, connection reset, or SSL handshake failure.

Common situations: SonarQube server has no egress to api.bitbucket.org (firewall, air-gapped installation); proxy not configured for the JVM; DNS outage; TLS-intercepting proxy with untrusted certificates; Bitbucket Cloud returning a connection-level failure during an outage; response read interrupted by socket timeout on large repository listings.

Related errors


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