SonarSource/sonarqube · error · AzureDevopsServerException
Invalid personal access token
Error message
Invalid personal access token
What it means
checkResponseIsSuccessful maps an Azure DevOps HTTP 401 response to AzureDevopsServerException(code=401, 'Invalid personal access token'). The configured token was rejected by the server as authentication failed while performing an Azure DevOps API call.
Source
Thrown at server/sonar-alm-client/src/main/java/org/sonar/alm/client/azure/AzureDevOpsHttpClient.java:237
throw new IllegalArgumentException(
String.format(UNABLE_TO_CONTACT_AZURE_SERVER_MESSAGE_FORMAT, UNABLE_TO_CONTACT_AZURE_SERVER, request.url(), e.getMessage()),
e);
}
}
protected static Request prepareRequestWithToken(String token, String method, HttpUrl url, @Nullable RequestBody body) {
return new Request.Builder()
.method(method, body)
.url(url)
.addHeader("Authorization", encodeToken("accessToken:" + token))
.build();
}
protected static void checkResponseIsSuccessful(Response response) throws IOException {
if (!response.isSuccessful()) {
if (response.code() == HttpURLConnection.HTTP_UNAUTHORIZED) {
LOG.error("{} for request [{}]: Invalid personal access token", UNABLE_TO_CONTACT_AZURE_SERVER, response.request().url());
throw new AzureDevopsServerException(response.code(), "Invalid personal access token");
}
if (response.code() == HttpURLConnection.HTTP_NOT_FOUND) {
LOG.error("{} for request [{}]: URL Not Found", UNABLE_TO_CONTACT_AZURE_SERVER, response.request().url());
throw new AzureDevopsServerException(response.code(), "Invalid Azure URL");
}
ResponseBody responseBody = response.body();
String body = responseBody.string();
String errorMessage = generateErrorMessage(body);
LOG.error("Azure API call to [{}] failed with {} http code. Azure response content : [{}]", response.request().url(), response.code(), body);
throw new AzureDevopsServerException(response.code(), errorMessage);
}
}
protected static String generateErrorMessage(String body) {
GsonAzureError gsonAzureError = null;
try {View on GitHub (pinned to 184c821202)
Solutions
- Generate a new PAT in Azure DevOps with the required scopes (e.g. Code Read, or full access for validation) and update it in SonarQube devops integration settings.
- Confirm the PAT's organization/expiration and that it has not been revoked.
- Ensure the Azure DevOps Server URL matches the collection/organization the PAT belongs to.
- Re-run the Azure DevOps integration validation after updating the token.
Example fix
// before pat=expired-token-... // after pat=<newly generated PAT with Code>Read scope>
Defensive patterns
Strategy: validation
Validate before calling
// validate PAT before configuring
Request req = new Request.Builder()
.url(azureUrl + "/_apis/projects?api-version=5.0")
.header("Authorization", Credentials.basic("", pat))
.build();
try (Response r = client.newCall(req).execute()) {
if (r.code() == 401) throw new IllegalArgumentException("PAT invalid or expired");
} Try / catch
try {
client.importAzureRepos(url, pat);
} catch (AzureDevopsServerException e) {
if (e.getCode() == HttpURLConnection.HTTP_UNAUTHORIZED) {
throw new ConfigurationException("Personal access token invalid/expired — regenerate it in Azure DevOps", e);
}
throw e;
} Prevention
- Set PAT expiry reminders; SonarQube does not rotate tokens automatically.
- Use the minimum required scopes (e.g. Code > Read) but ensure they cover the integration.
- Ensure the PAT belongs to the same organization/collection as the configured URL.
- Re-validate the integration immediately after rotating a PAT.
When it happens
Trigger: doGet/doCall receives a response with code 401 Unauthorized — the PAT is wrong, expired, revoked, lacks the required scopes, or was generated on a different organization/collection than the configured URL.
Common situations: PAT expired (default 30/90-day lifetimes), token rotated without updating SonarQube settings, PAT scoped to one organization but used against another, or a fine-grained token missing 'Code > Read' scope.
Related errors
- generateErrorMessage(body)
- %s for request [%s]: [%s]
- Response from Azure for request [%s] could not be parsed: [%
- Invalid Azure URL
- Invalid Azure URL or Personal Access Token
AI-assisted analysis of SonarSource/sonarqube@184c821202 (2026-09-09).
Data as JSON: /api/errors/ffee361f5e79fa40.
Report an issue: GitHub.