SonarSource/sonarqube · warning
GitLab returned an access token for project
Error message
GitLab returned an access token for project '{}' expiring on '{}', within the {}-day rotation margin: it will not be reused What it means
In getOrCreateToken(), after creating a fresh GitLab token, the provider checks whether its expiry falls within TOKEN_ROTATION_MARGIN_DAYS of now. If so, it logs this warning and returns the token WITHOUT caching it, so every request mints a new token instead of reusing a soon-to-expire one. This is intentional protective behavior, not a failure.
Solutions
- Increase the expiry configured for minted tokens on the GitLab side (instance access settings) so new tokens exceed the rotation margin.
- Check the GitLab instance's maximum allowed token lifetime and align SonarQube's requested expiry.
- Treat this as expected if short-lived tokens are intentional; no action needed, only throughput cost of re-minting.
- Upgrade GitLab/policy configuration to allow longer-lived tokens if performance of repeated mints matters.
Example fix
// before (GitLab token expiry set to 3 days, margin e.g. 30) expires_at: 2026-09-12 // after expires_at: 2027-09-09 // beyond rotation margin, token gets cached and reused
Defensive patterns
Strategy: fallback
Validate before calling
// Java
LocalDate expiresAt = LocalDate.parse(token.expiresAt());
boolean expiringSoon = !expiresAt.isAfter(LocalDate.now().plusDays(TOKEN_ROTATION_MARGIN_DAYS));
if (expiringSoon) { /* expect re-mint per call; plan GitLab token policy accordingly */ } Type guard
boolean exceedsRotationMargin(ScmAccessToken token) {
return LocalDate.parse(token.expiresAt()).isAfter(LocalDate.now().plusDays(marginDays));
} Prevention
- Configure GitLab token lifetime well above the rotation margin.
- Check the instance's max token expiration policy before rollout.
- Monitor this warning as a signal to lengthen token lifetimes.
- Budget for re-mint cost if short-lived tokens are intentional.
When it happens
Trigger: GitLab returns a personal access token whose expires_at is already close (within the rotation margin days) when createToken() runs — e.g. the GitLab instance's max token lifetime is short, or the admin set a short expiry on the PAT used for minting.
Common situations: GitLab instances configured with tight token lifetime limits; tokens created near a mandated expiry date; misconfigured instance-level token expiration policy.
Related errors
- allowAllGroups can only be enabled when Auto-provisioning…
- allowAllGroups cannot be enabled when the GitLab URL is…
- allowedGroups cannot be empty when Auto-provisioning is…
- Cannot mint a GitLab access token for project
- Cannot mint a GitLab access token: project
AI-assisted analysis of SonarSource/sonarqube@184c821202 (2026-09-09).
Data as JSON: /api/errors/e8b01c9178835991.
Report an issue: GitHub.
Appendix: source
Thrown at server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/gitlab/GitlabScmAccessTokenProvider.java:135
}
// GitLab API calls below are network I/O, deliberately made outside the DbSession above, so a
// pooled DB connection is not held for their duration.
return Optional.of(getOrCreateToken(request));
}
private ScmAccessToken getOrCreateToken(TokenMintRequest request) {
Optional<ScmAccessToken> cachedToken = getCachedToken(request.cacheKey);
if (cachedToken.isPresent()) {
return cachedToken.get();
}
Lock refreshLock = tokenRefreshLocks.get(request.cacheKey);
refreshLock.lock();
try {
return getCachedToken(request.cacheKey).orElseGet(() -> {
ScmAccessToken token = createToken(request);
if (isExpiring(token)) {
LOG.warn("GitLab returned an access token for project '{}' expiring on '{}', within the {}-day rotation margin: it will not be reused",
request.safeProjectKey, token.expiresAt(), TOKEN_ROTATION_MARGIN_DAYS);
return token;
}
tokenCache.put(request.cacheKey, token);
return token;
});
} finally {
refreshLock.unlock();
}
}
private Optional<ScmAccessToken> getCachedToken(TokenCacheKey cacheKey) {
ScmAccessToken token = tokenCache.getIfPresent(cacheKey);
if (token == null) {
return Optional.empty();
}
if (isExpiring(token)) {
tokenCache.invalidate(cacheKey);View on GitHub (pinned to 184c821202)