{"record":{"id":"0b44294f75910980","repo":"SonarSource/sonarqube","slug":"failed-to-fetch-azuredevops-repository-s-from-p","errorCode":null,"errorMessage":"Failed to fetch AzureDevOps repository '%s' from project '%s' from '%s'","messagePattern":"Failed to fetch AzureDevOps repository '(.+?)' from project '(.+?)' from '(.+?)'","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/azuredevops/AzureDevOpsProjectCreator.java","lineNumber":106,"sourceCode":"\n    ComponentCreationData componentCreationData = projectCreator.getOrCreateProject(dbSession, request);\n    ProjectDto projectDto = Optional.ofNullable(componentCreationData.projectDto()).orElseThrow();\n    createProjectAlmSettingDto(dbSession, repo, projectDto, almSettingDto, monorepo);\n    return componentCreationData;\n  }\n\n  private String findPersonalAccessTokenOrThrow(DbSession dbSession, AlmSettingDto almSettingDto) {\n    String userUuid = requireNonNull(userSession.getUuid(), \"User UUID cannot be null.\");\n    Optional<AlmPatDto> almPatDto = dbClient.almPatDao().selectByUserAndAlmSetting(dbSession, userUuid, almSettingDto);\n    return almPatDto.map(AlmPatDto::getPersonalAccessToken)\n      .orElseThrow(() -> new IllegalArgumentException(String.format(\"personal access token for '%s' is missing\", almSettingDto.getKey())));\n  }\n\n  private GsonAzureRepo fetchAzureDevOpsProject(String azureDevOpsUrl, String pat, String projectIdentifier, String repositoryIdentifier) {\n    try {\n      return azureDevOpsHttpClient.getRepo(azureDevOpsUrl, pat, projectIdentifier, repositoryIdentifier);\n    } catch (AzureDevopsServerException e) {\n      throw new IllegalStateException(format(\"Failed to fetch AzureDevOps repository '%s' from project '%s' from '%s'\", repositoryIdentifier, projectIdentifier, azureDevOpsUrl),\n        e);\n    }\n  }\n\n  private String getProjectKey(@Nullable String projectKey, GsonAzureRepo repository) {\n    return Optional.ofNullable(projectKey).orElseGet(() -> projectKeyGenerator.generateUniqueProjectKey(repository.getProject().getName(), repository.getName()));\n  }\n\n  private static String getProjectName(@Nullable String projectName, GsonAzureRepo repository) {\n    return Optional.ofNullable(projectName).orElse(repository.getName());\n  }\n\n  private void createProjectAlmSettingDto(DbSession dbSession, GsonAzureRepo repository, ProjectDto projectDto, AlmSettingDto almSettingDto, Boolean monorepo) {\n    ProjectAlmSettingDto projectAlmSettingDto = new ProjectAlmSettingDto()\n      .setAlmSettingUuid(almSettingDto.getUuid())\n      .setAlmRepo(repository.getName())\n      .setAlmSlug(repository.getProject().getName())\n      .setUrl(repository.getWebUrl())","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/SonarSource/sonarqube/blob/184c821202192afc1c599fc912d0889b69fffa53/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/azuredevops/AzureDevOpsProjectCreator.java#L88-L124","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)"],"exampleFix":"// before\nalmSetting url: https://dev.azure.com/org, repo: \"my Repo\"  // wrong name\n// after\nrepo: \"my-repo\"  // actual repository slug per Azure DevOps API","handlingStrategy":"validation","validationCode":"// validate Azure DevOps inputs before binding\nconst urlOk = /^https:\\/\\/.+/.test(azureDevOpsUrl);\nconst repoExists = await fetch(`${azureDevOpsUrl}/${project}/_apis/git/repositories/${repo}?api-version=6.0`, { headers: { Authorization: `Basic ${btoa(':' + pat)}` } }).then(r => r.ok);\nif (!urlOk || !repoExists) throw new Error('Azure DevOps repo unreachable: check URL, project, repo name, PAT');","typeGuard":"null","tryCatchPattern":"try {\n  createAzureDevOpsProject(...);\n} catch (IllegalStateException e) {\n  if (e.getMessage().startsWith(\"Failed to fetch AzureDevOps repository\")) {\n    // inspect cause (AzureDevopsServerException) for 401 vs 404 vs 5xx\n  } else throw e;\n}","preventionTips":["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"],"tags":["azure-devops","http","integration"],"backgroundTag":"http-request-failed","analyzedSha":"184c821202192afc1c599fc912d0889b69fffa53","analyzedAt":"2026-09-09T12:23:51.573Z","contentChangedAt":"2026-09-09T12:23:51.573Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}