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

  1. Verify the Azure DevOps URL, project name, and repository name exactly match Azure DevOps (case and collection path)
  2. Check the PAT has Code (Read) scope and has not expired; regenerate if needed
  3. Test the endpoint directly: GET {url}/{project}/_apis/git/repositories/{repo}?api-version=... with the PAT
  4. 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

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


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