SonarSource/sonarqube · critical · GitlabServerException
Invalid personal access token
Error message
Invalid personal access token
What it means
checkResponseIsSuccessful maps GitLab's HTTP 401 (Unauthorized) — when not identified as revoked or expired — to GitlabServerException 'Invalid personal access token'. The token presented does not authenticate: it is wrong, mistyped, or deleted.
Source
Thrown at server/sonar-alm-client/src/main/java/org/sonar/alm/client/gitlab/GitlabApplicationClient.java:222
checkResponseIsSuccessful(response, "GitLab Merge Request did not happen, please check your configuration");
}
protected static void checkResponseIsSuccessful(Response response, String errorMessage) throws IOException {
if (!response.isSuccessful()) {
String body = response.body().string();
LOG.error("Gitlab API call to [{}] failed with {} http code. gitlab response content : [{}]", response.request().url(), response.code(), body);
if (isTokenRevoked(response, body)) {
throw new GitlabServerException(response.code(), "Your GitLab token was revoked");
} else if (isTokenExpired(response, body)) {
throw new GitlabServerException(response.code(), "Your GitLab token is expired");
} else if (isInsufficientScope(response, body)) {
throw new GitlabServerException(response.code(), "Your GitLab token has insufficient scope");
} else if (response.code() == HTTP_FORBIDDEN) {
throw new GitlabServerException(response.code(), "Forbidden access to GitLab. Verify your token's permissions and IP restrictions.");
} else if (response.code() == HTTP_TOO_MANY_REQUESTS) {
throw new GitlabServerException(response.code(), "GitLab API rate limit exceeded. Try again later.");
} else if (response.code() == HTTP_UNAUTHORIZED) {
throw new GitlabServerException(response.code(), "Invalid personal access token");
} else if (response.isRedirect()) {
throw new GitlabServerException(response.code(), "Request was redirected, please provide the correct URL");
} else {
throw new GitlabServerException(response.code(), errorMessage);
}
}
}
private static boolean isTokenRevoked(Response response, String body) {
if (response.code() == HTTP_UNAUTHORIZED) {
try {
Optional<GsonError> gitlabError = GsonError.parseOne(body);
return gitlabError.map(GsonError::getErrorDescription).map(description -> description.contains("Token was revoked")).orElse(false);
} catch (JsonParseException e) {
// nothing to do
}
}
return false;View on GitHub (pinned to 184c821202)
Solutions
- Regenerate the PAT in GitLab and paste it again carefully (no leading/trailing spaces) into SonarQube's GitLab integration settings.
- Verify the token works against the configured URL: curl -H 'PRIVATE-TOKEN: <token>' <gitlab-url>/api/v4/user.
- Ensure the GitLab URL in SonarQube matches the instance the token was created on.
- Use a personal access token (not a deploy token or runner token) with 'api' and 'read_user' scopes.
Example fix
// before (truncated token) token = "glpat-AbC123"; // after (full token) token = "glpat-AbC123fullTokenValueFromGitLab";
Defensive patterns
Strategy: validation
Validate before calling
// Validate the token before saving it in SonarQube settings
Response r = call("GET", gitlabUrl + "/api/v4/user", token.trim());
if (r.code() == 401) throw new IllegalArgumentException("Personal access token is invalid for " + gitlabUrl + " — regenerate it and paste without whitespace"); Prevention
- Trim tokens on input and paste the full token value.
- Ensure the token is a PAT from the same GitLab instance as the configured URL.
- Use SonarQube's 'Test configuration' before saving credentials.
When it happens
Trigger: Any GitLab API call via checkResponseIsSuccessful (checkProjectAccess, checkToken, getPersonalAccessTokenInfo, checkWritePermission, createProjectAccessToken) receives 401 whose body does not match the revoked/expired signatures, at GitlabApplicationClient.java:222.
Common situations: Token copy-pasted with extra whitespace or truncation; wrong token kind used (e.g. deploy token instead of PAT); token from a different GitLab instance than the configured URL; token deleted while SonarQube still caches it.
Related errors
- Your GitLab token was revoked
- Your GitLab token is expired
- Your GitLab token has insufficient scope
- Invalid personal access token
- Authentication failed, verify the Client Id, Client Secret a
AI-assisted analysis of SonarSource/sonarqube@184c821202 (2026-09-09).
Data as JSON: /api/errors/3ad0c507920b5126.
Report an issue: GitHub.