GitLab: 401 "invalid_token" — "Token is expired" or "Token was revoked"
GitLab knows the token but no longer accepts it: it has expired, or it has been revoked. GitLab refuses with HTTP 401 and says which in error_description. An expired token cannot be reactivated; create a new one and put it where the old one was. Unlike a missing scope (403 insufficient_scope), nothing about the token's scopes will fix this.
The whole answer
As GitLab builds it (lib/api/api_guard.rb), for an expired token:
HTTP 401
WWW-Authenticate: Bearer realm="Protected by OAuth 2.0", error="invalid_token",
error_description="Token is expired. You can either do re-authorization or token refresh."
{"error": "invalid_token",
"error_description": "Token is expired. You can either do re-authorization or token refresh."}
The same answer from a public report (a package upload in 2026). The error_description is one of three:
- Token is expired. You can either do re-authorization or token refresh. The expiry date has passed.
- Token was revoked. You have to re-authorize from the user. Someone revoked it, or it was rotated (a public report on a self-managed server).
- Token is an impersonation token but impersonation was disabled. An administrator turned impersonation off.
A token GitLab does not know at all (mistyped, cut short when pasted, or made on another GitLab server) gets a plainer 401 instead: {"message": "401 Unauthorized"} (lib/api/helpers.rb). To see which answer you have, call the address that failed with curl:
curl -i --header "PRIVATE-TOKEN: your-token" "https://gitlab.com/api/v4/user"
Expired
From GitLab's personal access tokens page:
- Personal, group, project and impersonation access tokens expire at midnight UTC on the expiry date. Expired tokens cannot be reactivated: you must create a new token.
- A token created without an expiry date gets one 365 days from the day it was created, and by default the date cannot be more than 365 days away. So a token made without a date stops working a year later. On GitLab 17.6 and later, administrators can change the maximum lifetime.
- GitLab emails you seven days before a personal access token expires, and on GitLab 17.6 and later also 30 and 60 days before. The same page has an iCalendar feed of your tokens' expiry dates.
Revoked
- Revoke on the Personal access tokens page ends a token at once; an administrator can revoke it too.
- Rotate creates a new token with the same permissions and scope, and the original becomes inactive immediately: tools that use the original stop working until you give them the new one. So a token rotated by you, a colleague or a script answers "Token was revoked" everywhere it is still stored.
- Through the API, rotating a token that was already revoked revokes every active token of the same family (GitLab's automatic reuse detection). A script that rotates a token it no longer holds can end the new one too.
Create a new token
- In GitLab: your avatar in the upper-right corner, Edit profile, then Access > Personal access tokens in the left sidebar (the names in GitLab's docs on October 24, 2026; older versions differ). From Generate token, choose Legacy token.
- A name, an expiry date (up to 365 days away by default) and the same scopes as before. GitLab can open the form with them filled in, for example
https://gitlab.com/-/user_settings/personal_access_tokens?name=IDE&scopes=api(prefilling a token; on a Self-Managed server, use its address). - Copy the token. GitLab shows it only until you leave or refresh the page.
- Put it everywhere the old one was.
In IntelliJ IDEA: "Login failed. Check your API token or GitLab version."
When you log in with a token in the GitLab support built into IntelliJ IDEA, it first reads the server's version with that token: the GraphQL metadata query, and if that fails, the REST /version endpoint (GitLab: Metadata API). Both need a token GitLab accepts. In the source (checked October 30, 2026; IntelliJ IDEA: GitLabTokenLoginPanelModel.kt, GitLabSecurityUtil.kt, GitLabServersManager.kt, GitLabApi.kt, GitLabLoginErrorStatusPresenter.kt):
- "Login failed. Check your API token or GitLab version." when neither answers, whatever the reason. An expired, revoked or unknown token (the 401 answers above) ends here, but so does any other failure of that first request, because IntelliJ IDEA catches them all the same way: a server address that is not GitLab's, or a proxy in the way. The message does not say which. The
curlabove, with the same server address and token, shows GitLab's own answer. IntelliJ IDEA keeps a server's version until the IDE restarts, so when it has already read it in this session, a rejected token fails one step later, when the user is read, with a different message. - Where IntelliJ IDEA can log in with Git instead, the message goes on: "For GitLab verisons older than 14.0, log in using Git." (the spelling is IntelliJ IDEA's), and the link logs in with Git.
- "Login failed. GitLab versions older than 14.0 are not supported." when the version is read and is older than 14.0.
- The token login's link to a new token opens GitLab's Personal access tokens page with the scopes
apiandread_userfilled in. - "Token missing or expired, log in again" in Share Project on GitLab: shown when no token is saved for the account, or when loading the account's groups gets HTTP 401. The link opens the login again (IntelliJ IDEA: GitLabShareProjectDialogViewModel.kt).
GitLab merge requests and pipelines in IntelliJ IDEA
Lesstep makes two plugins for JetBrains IDEs 2024.1 or later that read GitLab with read_api and change things with api: Merge Request Inbox for GitLab and Pipeline Inbox for GitLab CI. When GitLab refuses the token with 401 while they read, they say that the token expired, was revoked or is from another GitLab instance, show GitLab's own description, and offer to open their settings, which have a link to GitLab's form with the name and scope filled in (GitLab 16.7 or later). The token is kept in the IDE's password safe, so replacing it there is the only change. Reading is free; approving, commenting and merging, or retrying, cancelling and running pipelines, are Pro (US$1.50 a month, with a 30-day trial).
GITLAB is a trademark of GitLab Inc.; IntelliJ IDEA is a trademark of JetBrains s.r.o. This page is not affiliated with or endorsed by GitLab Inc. or JetBrains.
More: GitLab "insufficient_scope" · GitLab 404 "Project Not Found" · GitLab merge refused (405, 409, 422) · GitLab approval refused (401, 409) · GitLab comment or thread refused (400, 403) · GitLab merge request not created (409, 422, 400) · GitLab rebase refused or failed (403, 409) · GitLab merge request list refused (400, 404) · GitLab merge request changes cut short ("1000+", too_large) · GitLab 429 "Retry later" (rate limits) · GitLab "compare_timeout": true (diff limits) · GitLab merge request plugins for IntelliJ IDEA, compared · GitLab pipelines in JetBrains IDEs, compared · 日本語