SonarSource/sonarqube · error · GitlabServerException
Forbidden access to GitLab. Verify your token's permissions
Error message
Forbidden access to GitLab. Verify your token's permissions and IP restrictions.
What it means
When a GitLab API call returns HTTP 403 (Forbidden) without a token-revocation/expiry/scope cause, checkResponseIsSuccessful throws GitlabServerException 'Forbidden access to GitLab. Verify your token's permissions and IP restrictions.' The token is valid but GitLab refuses the request, typically due to role level or IP allow-listing.
Source
Thrown at server/sonar-alm-client/src/main/java/org/sonar/alm/client/gitlab/GitlabApplicationClient.java:218
}
}
protected static void checkResponseIsSuccessful(Response response) throws IOException {
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) {View on GitHub (pinned to 184c821202)
Solutions
- Raise the token owner's role on the project to at least Maintainer.
- Add the SonarQube server IP to the GitLab group/project IP allow-list (Settings > General > IP restrictions) or remove the restriction.
- If SSO-enforced group, authorize the PAT for that group (Group Settings > SSO > token access).
- Test the same call manually with curl to see GitLab's message body, which is logged by SonarQube at ERROR level.
Example fix
// before: token owner role = Developer project_member_role: Developer // after project_member_role: Maintainer
Defensive patterns
Strategy: validation
Validate before calling
// Verify the token owner can write to the project before enabling decoration
Response r = call("GET", gitlabUrl + "/api/v4/projects/" + urlEncode(projectPath) + "/members/all/" + userId, token);
if (r.code() == 200) {
int level = getJson(r).get("access_level").getAsInt();
if (level < 40) throw new IllegalStateException("Requires Maintainer (40+) access level, got " + level);
} Prevention
- Grant the token owner at least Maintainer role on bound projects.
- Add the SonarQube server egress IP to GitLab IP restriction allow-lists.
- Authorize PATs for SSO-protected groups.
When it happens
Trigger: Any GitLab API call via checkResponseIsSuccessful returns 403 that does not match the insufficient_scope body pattern, e.g. role too low on the project or the caller IP blocked by GitLab's IP restriction settings (GitlabApplicationClient.java:218).
Common situations: Token owner is not Maintainer/Owner of the project; GitLab group/project IP allow-list excludes the SonarQube server's egress IP; group SSO enforcement blocks the token; GitLab rate/abuse protection flagging the server.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Could not validate GitLab write permission. Got an unexpecte
- Your GitLab token has insufficient scope
- Request was redirected, please provide the correct URL
- Error returned by Bitbucket Cloud: The OAuth client in the B
- Missing permissions; permission granted on %s
AI-assisted analysis of SonarSource/sonarqube@184c821202 (2026-09-09).
Data as JSON: /api/errors/171a34d1f09900a7.
Report an issue: GitHub.