{"record":{"id":"0e57cbf416229a19","repo":"SonarSource/sonarqube","slug":"cannot-provide-an-azure-devops-access-token-for-pr","errorCode":null,"errorMessage":"Cannot provide an Azure DevOps access token for project '%s': invalid Azure DevOps configuration: %s","messagePattern":"Cannot provide an Azure DevOps access token for project '(.+?)': invalid Azure DevOps configuration: (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":400,"severity":"error","filePath":"server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/azuredevops/AzureDevOpsScmAccessTokenProvider.java","lineNumber":119,"sourceCode":"\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 {\n      azureDevOpsValidator.validate(almSetting);\n    } catch (RuntimeException e) {\n      throw new IllegalArgumentException(\n        format(\"Cannot provide an Azure DevOps access token for project '%s': invalid Azure DevOps configuration: %s\", safeProjectKey, e.getMessage()), e);\n    }\n\n    String personalAccessToken = requireNonNull(almSetting.getDecryptedPersonalAccessToken(encryption), \"Azure DevOps personal access token cannot be null\");\n    // Azure DevOps PATs are long-lived, admin-managed credentials with their own separate expiry — not\n    // minted per call — so there is no per-request expiry to report here.\n    return new ScmAccessToken(ALM.AZURE_DEVOPS.getId(), GIT_USERNAME, personalAccessToken, null);\n  }\n\n  /**\n   * Strips CR/LF from the user-controlled project key before logging it, so a crafted value cannot\n   * forge extra log lines/entries (CWE-117).\n   */\n  private static String sanitizeForLog(String value) {\n    return CRLF_PATTERN.matcher(value).replaceAll(\"_\");\n  }\n}\n","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/SonarSource/sonarqube/blob/184c821202192afc1c599fc912d0889b69fffa53/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/azuredevops/AzureDevOpsScmAccessTokenProvider.java#L101-L137","documentation":"AzureDevOpsScmAccessTokenProvider.passThrough() validates the ALM binding before minting an SCM access token for a project. If AzureDevOpsValidator.validate() rejects the AlmSettingDto (bad URL, missing PAT, malformed configuration), it rethrows as IllegalArgumentException with the project key and underlying validation message embedded.","triggerScenarios":"Requesting an Azure DevOps access token for a project whose alm_pats/alm_settings binding is misconfigured: null or blank personal access token, invalid endpoint URL, or any validator RuntimeException (including NPE from missing URL/PAT via requireNonNull).","commonSituations":"Administrator deleted or cleared the Azure DevOps PAT; alm_settings entry created via API with missing fields; encryption key changed so decrypted PAT comes back null.","solutions":["Fix the Azure DevOps ALM binding in Administration > DevOps Platform Integrations: set a valid URL and a PAT with Code Read scope","Re-enter the PAT so it is re-encrypted with the current encryption key, then retry","Call the API only after the binding validates, e.g. run the Azure DevOps integration's validation beforehand","Check the embedded cause message in the error for the exact validation failure"],"exampleFix":"// before\nalmSettingDto with personalAccessToken = null\n// after\nalmSettingDto.setPersonalAccessToken(encryption.encrypt(pat))  // valid PAT with Code Read scope","handlingStrategy":"validation","validationCode":"// pre-validate the ALM binding before requesting tokens\nconst alm = await getAlmSetting('azure');\nif (!alm?.url || !/^https:\\/\\//.test(alm.url) || !alm.personalAccessTokenExists) {\n  throw new Error('Azure DevOps binding incomplete: set endpoint URL and PAT first');\n}","typeGuard":"null","tryCatchPattern":"try {\n  const token = scmAccessTokenProvider.getAccessToken(projectKey);\n} catch (IllegalArgumentException e) {\n  if (e.getMessage().contains(\"invalid Azure DevOps configuration\")) {\n    // fix alm_settings binding; cause holds the validation detail\n  } else throw e;\n}","preventionTips":["Verify the DevOps Platform integration validates (URL + PAT) before projects reference it","Re-enter PATs after encryption key changes","Check alm_settings rows for null/blank fields after scripted setup","Pin PAT scope to Code Read and track expiry"],"tags":["azure-devops","configuration","validation"],"backgroundTag":"invalid-config-value","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"}