{"record":{"id":"82176ed41eea55d7","repo":"SonarSource/sonarqube","slug":"cannot-provide-an-azure-devops-access-token-unkno","errorCode":null,"errorMessage":"Cannot provide an Azure DevOps access token: unknown project '{}'","messagePattern":"Cannot provide an Azure DevOps access token: unknown project '(.+?)'","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/azuredevops/AzureDevOpsScmAccessTokenProvider.java","lineNumber":98,"sourceCode":"    Optional<AlmSettingDto> resolvedAlmSetting;\n    try (DbSession dbSession = dbClient.openSession(false)) {\n      resolvedAlmSetting = resolveAzureDevOpsAlmSetting(dbSession, projectKey, safeProjectKey);\n    }\n\n    // Validation below is network I/O (an Azure DevOps API call), deliberately made outside the\n    // DbSession above — see GitlabScmAccessTokenProvider for the same rationale.\n    return resolvedAlmSetting.map(almSetting -> passThrough(safeProjectKey, almSetting));\n  }\n\n  /**\n   * Walks project -&gt; its DevOps Platform binding -&gt; the bound {@link AlmSettingDto}, short-circuiting\n   * to {@link Optional#empty()} (with a warning) at whichever step is missing, or once the binding\n   * turns out not to be Azure DevOps.\n   */\n  private Optional<AlmSettingDto> resolveAzureDevOpsAlmSetting(DbSession dbSession, String projectKey, String safeProjectKey) {\n    Optional<ProjectDto> project = dbClient.projectDao().selectProjectByKey(dbSession, projectKey);\n    if (project.isEmpty()) {\n      LOG.warn(\"Cannot provide an Azure DevOps access token: unknown project '{}'\", safeProjectKey);\n      return Optional.empty();\n    }\n\n    Optional<ProjectAlmSettingDto> projectAlmSetting = dbClient.projectAlmSettingDao().selectByProject(dbSession, project.get());\n    if (projectAlmSetting.isEmpty()) {\n      LOG.warn(\"Cannot provide an Azure DevOps access token: project '{}' is not bound to any DevOps Platform\", safeProjectKey);\n      return Optional.empty();\n    }\n\n    return dbClient.almSettingDao().selectByUuid(dbSession, projectAlmSetting.get().getAlmSettingUuid())\n      .filter(almSetting -> almSetting.getAlm() == ALM.AZURE_DEVOPS);\n  }\n\n  private ScmAccessToken passThrough(String safeProjectKey, AlmSettingDto almSetting) {\n    // AzureDevOpsValidator.validate() can fail with either IllegalArgumentException (bad config) or\n    // NullPointerException (missing URL/PAT via requireNonNull) — caught here as RuntimeException,\n    // rather than naming NullPointerException explicitly, to avoid catching it as a control-flow signal.\n    try {","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/SonarSource/sonarqube/blob/184c821202192afc1c599fc912d0889b69fffa53/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/azuredevops/AzureDevOpsScmAccessTokenProvider.java#L80-L116","documentation":"AzureDevOpsScmAccessTokenProvider.resolveAzureDevOpsAlmSetting logs this warning and returns Optional.empty() when selectProjectByKey finds no project for the given project key. mint() then completes without providing an Azure DevOps access token. This is a deliberate soft-fail (documented in the Javadoc: return empty with a warning at whichever step is missing), not an exception — the caller receives an empty result and typically surfaces 'no token available'.","triggerScenarios":"Calling the Azure DevOps SCM access token mint endpoint/API with a projectKey that has no corresponding row in the projects table (project deleted, key typo, or key from another SonarQube instance).","commonSituations":"CI pipeline passes a project key from a renamed/deleted SonarQube project; typo in projectKey in CI config; calling the token endpoint against the wrong SonarQube server; project key changed after a re-key operation while CI config still uses the old key.","solutions":["Verify the project key exists via GET api/projects/search?q=<key> and correct it in your CI/tooling configuration.","Recreate the project or restore it if it was deleted.","Confirm you are calling the correct SonarQube instance (URL/base path) where the project exists.","Handle the empty Optional in caller code by failing the pipeline with a clear 'project not found' message instead of a confusing token error."],"exampleFix":"// before\ncurl -u token: \"$SQ_URL/api/alm_integrations/get_azure_devops_scm_access_token?projectKey=old-key\"\n// after\n# resolve current key first\nKEY=$(curl -s -u token: \"$SQ_URL/api/projects/search?q=my-app\" | jq -r '.components[0].key')\ncurl -u token: \"$SQ_URL/api/alm_integrations/get_azure_devops_scm_access_token?projectKey=$KEY\"","handlingStrategy":"validation","validationCode":"# Resolve and verify the project key before requesting a token\nKEY=$(curl -s -u \"$TOKEN:\" \"$SQ_URL/api/projects/search?q=$RAW_KEY\" | jq -r '.components[0].key // empty')\nif [ -z \"$KEY\" ]; then echo \"Project not found; fix projectKey\" >&2; exit 1; fi","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always resolve project keys via api/projects/search instead of hardcoding stale keys.","Fail CI early with an explicit 'project not found' message when the token endpoint returns empty.","Pin environment-specific keys per server (staging vs production)."],"tags":["azure-devops","project-not-found","alm-integration","scm-token"],"backgroundTag":"resource-not-found","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"}