SonarSource/sonarqube · error · IllegalStateException
Failed to fetch AzureDevOps repository '%s' from project '%s
Error message
Failed to fetch AzureDevOps repository '%s' from project '%s' from '%s'
What it means
AzureDevOpsProjectCreator.fetchAzureDevOpsProject() wraps failures of azureDevOpsHttpClient.getRepo() in an IllegalStateException with full context (repository, project, Azure DevOps URL). The underlying AzureDevopsServerException indicates Azure DevOps returned an error when queried for the repository.
Source
Thrown at server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/azuredevops/AzureDevOpsProjectCreator.java:106
ComponentCreationData componentCreationData = projectCreator.getOrCreateProject(dbSession, request);
ProjectDto projectDto = Optional.ofNullable(componentCreationData.projectDto()).orElseThrow();
createProjectAlmSettingDto(dbSession, repo, projectDto, almSettingDto, monorepo);
return componentCreationData;
}
private String findPersonalAccessTokenOrThrow(DbSession dbSession, AlmSettingDto almSettingDto) {
String userUuid = requireNonNull(userSession.getUuid(), "User UUID cannot be null.");
Optional<AlmPatDto> almPatDto = dbClient.almPatDao().selectByUserAndAlmSetting(dbSession, userUuid, almSettingDto);
return almPatDto.map(AlmPatDto::getPersonalAccessToken)
.orElseThrow(() -> new IllegalArgumentException(String.format("personal access token for '%s' is missing", almSettingDto.getKey())));
}
private GsonAzureRepo fetchAzureDevOpsProject(String azureDevOpsUrl, String pat, String projectIdentifier, String repositoryIdentifier) {
try {
return azureDevOpsHttpClient.getRepo(azureDevOpsUrl, pat, projectIdentifier, repositoryIdentifier);
} catch (AzureDevopsServerException e) {
throw new IllegalStateException(format("Failed to fetch AzureDevOps repository '%s' from project '%s' from '%s'", repositoryIdentifier, projectIdentifier, azureDevOpsUrl),
e);
}
}
private String getProjectKey(@Nullable String projectKey, GsonAzureRepo repository) {
return Optional.ofNullable(projectKey).orElseGet(() -> projectKeyGenerator.generateUniqueProjectKey(repository.getProject().getName(), repository.getName()));
}
private static String getProjectName(@Nullable String projectName, GsonAzureRepo repository) {
return Optional.ofNullable(projectName).orElse(repository.getName());
}
private void createProjectAlmSettingDto(DbSession dbSession, GsonAzureRepo repository, ProjectDto projectDto, AlmSettingDto almSettingDto, Boolean monorepo) {
ProjectAlmSettingDto projectAlmSettingDto = new ProjectAlmSettingDto()
.setAlmSettingUuid(almSettingDto.getUuid())
.setAlmRepo(repository.getName())
.setAlmSlug(repository.getProject().getName())
.setUrl(repository.getWebUrl())View on GitHub (pinned to 184c821202)
Solutions
- Verify the Azure DevOps URL, project name, and repository name exactly match Azure DevOps (case and collection path)
- Check the PAT has Code (Read) scope and has not expired; regenerate if needed
- Test the endpoint directly: GET {url}/{project}/_apis/git/repositories/{repo}?api-version=... with the PAT
- Confirm SonarQube server can reach the Azure DevOps host (network, proxy, TLS trust store)
Example fix
// before almSetting url: https://dev.azure.com/org, repo: "my Repo" // wrong name // after repo: "my-repo" // actual repository slug per Azure DevOps API
Defensive patterns
Strategy: validation
Validate before calling
// validate Azure DevOps inputs before binding
const urlOk = /^https:\/\/.+/.test(azureDevOpsUrl);
const repoExists = await fetch(`${azureDevOpsUrl}/${project}/_apis/git/repositories/${repo}?api-version=6.0`, { headers: { Authorization: `Basic ${btoa(':' + pat)}` } }).then(r => r.ok);
if (!urlOk || !repoExists) throw new Error('Azure DevOps repo unreachable: check URL, project, repo name, PAT'); Type guard
null
Try / catch
try {
createAzureDevOpsProject(...);
} catch (IllegalStateException e) {
if (e.getMessage().startsWith("Failed to fetch AzureDevOps repository")) {
// inspect cause (AzureDevopsServerException) for 401 vs 404 vs 5xx
} else throw e;
} Prevention
- Validate repository/project names against the Azure DevOps REST API before binding
- Ensure PATs have Code Read scope and valid expiry
- Confirm server-to-server network reachability and TLS trust before rollout
- Use exact collection/organization URLs, not shortened aliases
When it happens
Trigger: Calling the ALM/project-creation flow (creating or importing an Azure DevOps project) where the configured URL, project identifier, or repository identifier does not resolve on the Azure DevOps server, or the PAT lacks permission / the server is unreachable.
Common situations: Typo in repository or project name during binding; Azure DevOps Server URL pointing at wrong collection; PAT missing Code Read scope; network/firewall blocking the SonarQube server from Azure DevOps.
Understand the failure class
Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.
Related errors
- Error while executing a call to %s. Return code %s. Error me
- SonarQube was not able to retrieve resources from external s
- generateErrorMessage(body)
- Invalid Azure URL
- GitLab Merge Request did not happen, please check your confi
AI-assisted analysis of SonarSource/sonarqube@184c821202 (2026-09-09).
Data as JSON: /api/errors/0b44294f75910980.
Report an issue: GitHub.