SonarSource/sonarqube · error · IllegalStateException

No uuid found for Bitbucket Cloud repository

Error message

No uuid found for Bitbucket Cloud repository '%s'

What it means

SonarQube throws this when a Bitbucket Cloud repository response lacks a 'uuid' field. The uuid is stored as the external repository id in the project binding, so resolution cannot proceed without it. It indicates an unexpected/deficient API payload.

Solutions

  1. Verify GET /2.0/repositories/{workspace}/{repo} includes a uuid field for the repo
  2. Retry the binding resolution — the error can stem from a transient bad response
  3. Check any proxies or Bitbucket integrations that might alter response bodies
  4. Update SonarQube to a version compatible with the current Bitbucket Cloud API
Defensive patterns

Strategy: retry

Validate before calling

const res = await fetch(`https://api.bitbucket.org/2.0/repositories/${workspace}/${repo}`, { headers: { Authorization: `Bearer ${token}` } });
const data = await res.json();
if (!data.uuid) {
  throw new Error(`Repo ${workspace}/${repo} response lacks uuid — check API version/proxies`);
}

Type guard

function hasUuid(repo) {
  return typeof repo?.uuid === 'string' && repo.uuid.length > 0;
}

Try / catch

try {
  resolution = resolveLive(almSetting, projectAlmSetting);
} catch (IllegalStateException e) {
  if (e.getMessage().contains("No uuid found")) {
    // transient/bad payload: retry once, then surface API-response diagnostics
    resolution = retryResolveOnce(almSetting, projectAlmSetting);
  } else { throw e; }
}

Prevention

When it happens

Trigger: resolveLive -> resolveBitbucketCloud gets a Repository whose getUuid() is null after getRepoWithAccessToken() succeeds — the html link exists but the uuid is absent from the API response.

Common situations: Bitbucket Cloud API changes or partial responses; proxy/gateway stripping fields; extremely old cached responses; repo metadata corruption on the Bitbucket side.

Understand the failure class

Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.

Related errors


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

Appendix: source

Thrown at server/sonar-webserver-webapi/src/main/java/org/sonar/server/almsettings/ws/ProjectBindingsServiceServerImpl.java:336

    return new LiveResolution(url, String.valueOf(repository.getId()));
  }

  private LiveResolution resolveBitbucketCloud(AlmSettingDto almSetting, ProjectAlmSettingDto projectAlmSetting, Map<String, String> tokenCache) {
    String clientId = requireNonNull(almSetting.getClientId(), CLIENT_ID_CANNOT_BE_NULL);
    String clientSecret = requireNonNull(almSetting.getDecryptedClientSecret(encryption), CLIENT_SECRET_CANNOT_BE_NULL);
    String workspace = requireNonNull(almSetting.getAppId(), WORKSPACE_CANNOT_BE_NULL);
    String almRepo = requireNonNull(projectAlmSetting.getAlmRepo(), ALM_REPO_CANNOT_BE_NULL);
    // The OAuth token is workspace-wide, not per-repository: reuse it across every binding of this ALM setting
    // resolved within the same request instead of minting a fresh one per binding.
    String accessToken = tokenCache.computeIfAbsent(almSetting.getUuid(), uuid -> bitbucketCloudRestClient.createAccessToken(clientId, clientSecret));
    org.sonar.alm.client.bitbucket.bitbucketcloud.Repository repository = bitbucketCloudRestClient.getRepoWithAccessToken(accessToken, workspace, almRepo);
    String url = repository.getHtmlHref();
    if (url == null) {
      throw new IllegalStateException(format("No html link found for Bitbucket Cloud repository '%s'", sanitizeForLog(almRepo)));
    }
    String repoId = repository.getUuid();
    if (repoId == null) {
      throw new IllegalStateException(format("No uuid found for Bitbucket Cloud repository '%s'", sanitizeForLog(almRepo)));
    }
    return new LiveResolution(url, repoId);
  }

  /**
   * GitHub's installation-token "repositories" scoping parameter expects the bare repository name
   * (no {@code owner/} prefix), unlike {@code almRepo} which is stored as {@code owner/repo}.
   */
  private static String bareRepositoryName(String almRepo) {
    int lastSlash = almRepo.lastIndexOf('/');
    return lastSlash < 0 ? almRepo : almRepo.substring(lastSlash + 1);
  }

  /**
   * Strips CR/LF from ALM-supplied identifiers (repo/project keys, slugs) before they're embedded in an exception
   * message that ends up in a log line — otherwise a crafted value could forge extra log entries (CWE-117). Only
   * used for messages; the real, unsanitized value is always what's sent to the ALM REST clients.
   */

View on GitHub (pinned to 184c821202)