SonarSource/sonarqube · critical · GitlabServerException
Your GitLab token is expired
Error message
Your GitLab token is expired
What it means
checkResponseIsSuccessful maps a failing GitLab response whose body indicates an expired token to GitlabServerException 'Your GitLab token is expired'. GitLab personal access tokens can carry an expiry date; once passed, all API calls fail with 401 and this error is surfaced so the operator knows the token must be renewed, not that permissions are wrong.
Solutions
- Renew the personal access token in GitLab (User Settings > Access Tokens > extend expiry or create a new one).
- Update the new token in SonarQube's GitLab integration settings and re-test the configuration.
- Consider GitLab's non-expiring token option (if instance policy allows) or a service-account token with a long expiry plus rotation reminders.
- Use GitLab project access tokens with automated rotation if the instance supports it.
Example fix
// GitLab: PAT expiring expires_at: 2024-01-01 // after: extend or recreate expires_at: 2027-01-01 (or 'no expiration' where policy permits)
Defensive patterns
Strategy: validation
Validate before calling
// Check token expiry before use
JsonObject info = getJson(gitlabUrl + "/api/v4/personal_access_tokens/self", token);
String expiresAt = info.get("expires_at").isJsonNull() ? null : info.get("expires_at").getAsString();
if (expiresAt != null && LocalDate.parse(expiresAt).isBefore(LocalDate.now().plusDays(7))) {
log.warn("GitLab PAT expires on {} — renew it soon", expiresAt);
} Prevention
- Set calendar reminders before the PAT expiry date.
- Prefer no-expiry or long-expiry tokens where instance policy allows, with scheduled rotation.
- Call /personal_access_tokens/self periodically to surface approaching expiry.
When it happens
Trigger: Any GitLab API call via checkResponseIsSuccessful (checkProjectAccess, checkToken, getPersonalAccessTokenInfo, checkWritePermission, createProjectAccessToken) returns 401 with a body matching the expired-token signature at GitlabApplicationClient.java:214.
Common situations: PAT reached its expiration date configured in GitLab; GitLab instance enforces token expiry policies; a long-lived SonarQube binding outlived the token's validity window.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Invalid personal access token
- Your GitLab token was revoked
- Your GitLab token has insufficient scope
- Authentication failed
- Authentication failed, verify the Client Id, Client Secret…
AI-assisted analysis of SonarSource/sonarqube@184c821202 (2026-09-09).
Data as JSON: /api/errors/7b9a87dde137370d.
Report an issue: GitHub.
Appendix: source
Thrown at server/sonar-alm-client/src/main/java/org/sonar/alm/client/gitlab/GitlabApplicationClient.java:214
try {
return URLEncoder.encode(value, UTF_8.toString());
} catch (UnsupportedEncodingException ex) {
throw new IllegalStateException(ex.getCause());
}
}
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) {View on GitHub (pinned to 184c821202)