SonarSource/sonarqube · error · IllegalStateException
No self link found for Bitbucket Server repository
Error message
No self link found for Bitbucket Server repository '%s'
What it means
SonarQube throws this when resolving a live project binding for a Bitbucket Server repository whose REST response lacks a 'self' href link. The self link is required to build the canonical repository URL stored in the binding. It indicates the Bitbucket Server instance returned a repository payload without the expected links.
Solutions
- Verify the repository exists on the Bitbucket Server instance and that almRepo/almSlug match it exactly
- Check the Bitbucket Server REST response for the repo (GET /rest/api/1.0/projects/{project}/repos/{slug}) and confirm links.self is present
- Upgrade or repair the Bitbucket Server instance if links are missing from responses
- Re-create the ALM setting with the correct server URL and re-bind the project
Example fix
// before
{ "almRepo": "repo", "almSlug": "wrong-slug" }
// after
{ "almRepo": "repo", "almSlug": "correct-slug" } Defensive patterns
Strategy: try-catch
Validate before calling
// Before binding, verify the repo's self link via the Bitbucket Server API:
const res = await fetch(`${serverUrl}/rest/api/1.0/projects/${proj}/repos/${slug}`);
const repo = await res.json();
if (!repo.links?.self?.find(l => l.href)) {
throw new Error(`Bitbucket Server repo ${slug} exposes no self link; fix repo/SLUG or server version before binding`);
} Type guard
function hasSelfHref(repo) {
return typeof repo?.links?.self?.[0]?.href === 'string' && repo.links.self[0].href.length > 0;
} Try / catch
try {
resolveLive(almSetting, projectAlmSetting);
} catch (IllegalStateException e) {
if (e.getMessage().contains("No self link found for Bitbucket Server repository")) {
log.error("Check almRepo/almSlug and Bitbucket Server version; links.self missing in REST response", e);
// surface actionable guidance or skip this binding
} else { throw e; }
} Prevention
- Validate almRepo/almSlug against the Bitbucket Server API before creating the binding
- Pin a Bitbucket Server version known to return links.self for repo payloads
- Test one binding resolution after any Bitbucket Server upgrade
- Log the raw REST response when resolution fails to diagnose missing links
When it happens
Trigger: Calling the project binding resolution flow (resolveLive -> resolveBitbucketServer) when bitbucketServerRestClient.getRepo() returns a Repository whose getSelfHref() is null — e.g. the configured almRepo/almSlug point at a repo whose links object is missing or the Bitbucket Server version returns a nonstandard links payload.
Common situations: Misconfigured almRepo/almSlug values pointing at an unexpected repo; older or customized Bitbucket Server versions omitting links.self; proxy or plugin modifying the REST response.
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
- No html link found for Bitbucket Cloud repository
- Cannot mint a GitLab access token: project
- Cannot mint a GitLab access token: project
- Failed to check permissions with Github, check the…
- Failed to construct Bitbucket Server repository URL for ALM…
AI-assisted analysis of SonarSource/sonarqube@184c821202 (2026-09-09).
Data as JSON: /api/errors/92712a3a3d9442d7.
Report an issue: GitHub.
Appendix: source
Thrown at server/sonar-webserver-webapi/src/main/java/org/sonar/server/almsettings/ws/ProjectBindingsServiceServerImpl.java:316
String url = requireNonNull(almSetting.getUrl(), URL_CANNOT_BE_NULL);
String almSlug = requireNonNull(projectAlmSetting.getAlmSlug(), ALM_SLUG_CANNOT_BE_NULL);
String almRepo = requireNonNull(projectAlmSetting.getAlmRepo(), ALM_REPO_CANNOT_BE_NULL);
GsonAzureRepo repository = azureDevOpsHttpClient.getRepo(url, pat, almSlug, almRepo);
String safeAlmRepo = sanitizeForLog(almRepo);
String repoUrl = requireNonNull(repository.getWebUrl(), format("Azure DevOps returned no web URL for repository '%s'", safeAlmRepo));
String repoId = requireNonNull(repository.getId(), format("Azure DevOps returned no id for repository '%s'", safeAlmRepo));
return new LiveResolution(repoUrl, repoId);
}
private LiveResolution resolveBitbucketServer(AlmSettingDto almSetting, ProjectAlmSettingDto projectAlmSetting) {
String pat = requireNonNull(almSetting.getDecryptedPersonalAccessToken(encryption), PAT_CANNOT_BE_NULL);
String serverUrl = requireNonNull(almSetting.getUrl(), URL_CANNOT_BE_NULL);
String almRepo = requireNonNull(projectAlmSetting.getAlmRepo(), ALM_REPO_CANNOT_BE_NULL);
String almSlug = requireNonNull(projectAlmSetting.getAlmSlug(), ALM_SLUG_CANNOT_BE_NULL);
org.sonar.alm.client.bitbucketserver.Repository repository = bitbucketServerRestClient.getRepo(serverUrl, pat, almRepo, almSlug);
String url = repository.getSelfHref();
if (url == null) {
throw new IllegalStateException(format("No self link found for Bitbucket Server repository '%s'", sanitizeForLog(almSlug)));
}
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();View on GitHub (pinned to 184c821202)