[BUG] GitLab OAuth token not auto-refreshed on 401 during session #6992

Open
opened 2026-02-16 18:05:50 -05:00 by yindo · 13 comments
Owner

Originally created by @VilgelmOne on GitHub (Jan 20, 2026).

Description

When using GitLab Duo with OAuth authentication, if the access token expires during an active opencode session, the error GitLabError: Failed to get direct access token: 401 Unauthorized - {"error":"invalid_token","error_description":"Token is expired. You can either do re-authorization or token refresh."} occurs.

Restarting opencode fixes the issue because the loader function in @gitlab/opencode-gitlab-auth plugin has auto-refresh logic that runs at startup.

Root Cause

The GitLab auth plugin (@gitlab/opencode-gitlab-auth@1.3.0) has a refreshTokenIfNeeded() function that correctly handles token refresh, but it's only called in the loader() function during provider initialization.

When the token expires mid-session:

  1. The GitLab API returns 401
  2. opencode doesn't catch this error and trigger a token refresh
  3. The user has to restart opencode

Expected Behavior

opencode should intercept 401 "invalid_token" errors from GitLab API and automatically:

  1. Call the refresh token flow
  2. Retry the failed request with the new token

Workaround

Restart opencode — the token will be refreshed on startup.

Environment

  • opencode version: 1.1.28
  • @gitlab/opencode-gitlab-auth: 1.3.0
  • OS: macOS

auth.json structure

{
  "gitlab": {
    "type": "oauth",
    "access": "...",
    "refresh": "...",
    "expires": 1768953223555,
    "enterpriseUrl": "https://gitlab.com"
  }
}

Suggestion

Add retry logic with token refresh in the GitLab provider's custom fetch or in the SDK error handling:

// In provider.ts gitlab custom loader
gitlab: async (input) => {
  // ... existing code ...
  return {
    autoload: !!apiKey,
    options: {
      // ... existing options ...
      fetch: async (input, init) => {
        const response = await fetch(input, init)
        if (response.status === 401) {
          const body = await response.clone().json().catch(() => ({}))
          if (body.error === 'invalid_token') {
            // Trigger token refresh and retry
            await refreshGitLabToken(providerID)
            // Retry with new token
            const newAuth = await Auth.get(providerID)
            if (newAuth?.type === 'oauth') {
              const headers = new Headers(init?.headers)
              headers.set('Authorization', `Bearer ${newAuth.access}`)
              return fetch(input, { ...init, headers })
            }
          }
        }
        return response
      }
    }
  }
}
Originally created by @VilgelmOne on GitHub (Jan 20, 2026). ### Description When using GitLab Duo with OAuth authentication, if the access token expires during an active opencode session, the error `GitLabError: Failed to get direct access token: 401 Unauthorized - {"error":"invalid_token","error_description":"Token is expired. You can either do re-authorization or token refresh."}` occurs. Restarting opencode fixes the issue because the `loader` function in `@gitlab/opencode-gitlab-auth` plugin has auto-refresh logic that runs at startup. ### Root Cause The GitLab auth plugin (`@gitlab/opencode-gitlab-auth@1.3.0`) has a `refreshTokenIfNeeded()` function that correctly handles token refresh, but it's only called in the `loader()` function during provider initialization. When the token expires mid-session: 1. The GitLab API returns 401 2. opencode doesn't catch this error and trigger a token refresh 3. The user has to restart opencode ### Expected Behavior opencode should intercept 401 "invalid_token" errors from GitLab API and automatically: 1. Call the refresh token flow 2. Retry the failed request with the new token ### Workaround Restart opencode — the token will be refreshed on startup. ### Environment - opencode version: 1.1.28 - @gitlab/opencode-gitlab-auth: 1.3.0 - OS: macOS ### auth.json structure ```json { "gitlab": { "type": "oauth", "access": "...", "refresh": "...", "expires": 1768953223555, "enterpriseUrl": "https://gitlab.com" } } ``` ### Suggestion Add retry logic with token refresh in the GitLab provider's custom fetch or in the SDK error handling: ```typescript // In provider.ts gitlab custom loader gitlab: async (input) => { // ... existing code ... return { autoload: !!apiKey, options: { // ... existing options ... fetch: async (input, init) => { const response = await fetch(input, init) if (response.status === 401) { const body = await response.clone().json().catch(() => ({})) if (body.error === 'invalid_token') { // Trigger token refresh and retry await refreshGitLabToken(providerID) // Retry with new token const newAuth = await Auth.get(providerID) if (newAuth?.type === 'oauth') { const headers = new Headers(init?.headers) headers.set('Authorization', `Bearer ${newAuth.access}`) return fetch(input, { ...init, headers }) } } } return response } } } } ```
Author
Owner

@github-actions[bot] commented on GitHub (Jan 20, 2026):

This issue might be a duplicate of existing issues. Please check:

  • #9360: Auto-Relogin for Anthropic OAuth via Persistent Browser Sessions - proposes a general solution for handling expired OAuth tokens that could apply to GitLab as well
  • #8655: GitLab duo provider is not working with Self Hosted instances - related GitLab OAuth/token issue

Feel free to ignore if none of these address your specific case.

@github-actions[bot] commented on GitHub (Jan 20, 2026): This issue might be a duplicate of existing issues. Please check: - #9360: Auto-Relogin for Anthropic OAuth via Persistent Browser Sessions - proposes a general solution for handling expired OAuth tokens that could apply to GitLab as well - #8655: GitLab duo provider is not working with Self Hosted instances - related GitLab OAuth/token issue Feel free to ignore if none of these address your specific case.
Author
Owner

@vglafirov commented on GitHub (Jan 21, 2026):

Thanks for opening the issue and suggested fix. I knew about this problem. It was in my todo list. I will try to get this resolved asap.

@vglafirov commented on GitHub (Jan 21, 2026): Thanks for opening the issue and suggested fix. I knew about this problem. It was in my todo list. I will try to get this resolved asap.
Author
Owner

@nicehiro commented on GitHub (Jan 21, 2026):

Just confirmed the issue existed when using ChatGPT OAuth authentication.

@nicehiro commented on GitHub (Jan 21, 2026): Just confirmed the issue existed when using ChatGPT OAuth authentication.
Author
Owner

@vglafirov commented on GitHub (Jan 21, 2026):

@nicehiro Not sure what you mean. The issue isn't related to GitLab Duo provider?

@vglafirov commented on GitHub (Jan 21, 2026): @nicehiro Not sure what you mean. The issue isn't related to GitLab Duo provider?
Author
Owner

@nicehiro commented on GitHub (Jan 21, 2026):

@nicehiro Not sure what you mean. The issue isn't related to GitLab Duo provider?

I’m not sure whether this is related to the GitLab Duo provider, but I’m seeing the same error (Error: Token refresh failed: 401) when using ChatGPT OAuth authentication. It was working normally moments ago, and then it suddenly started failing with this error.

@nicehiro commented on GitHub (Jan 21, 2026): > [@nicehiro](https://github.com/nicehiro) Not sure what you mean. The issue isn't related to GitLab Duo provider? I’m not sure whether this is related to the GitLab Duo provider, but I’m seeing the same error (`Error: Token refresh failed: 401`) when using ChatGPT OAuth authentication. It was working normally moments ago, and then it suddenly started failing with this error.
Author
Owner

@vglafirov commented on GitHub (Jan 21, 2026):

Let me clarify. You use ChatGPT Oauth to authenticate with OpenAI provider and it thrown 401 immediately or after some time, when token expires? Or you are trying to authenticate to Gitlab Duo, using ChatGPT OAuth? 🤔

@vglafirov commented on GitHub (Jan 21, 2026): Let me clarify. You use ChatGPT Oauth to authenticate with OpenAI provider and it thrown 401 immediately or after some time, when token expires? Or you are trying to authenticate to Gitlab Duo, using ChatGPT OAuth? 🤔
Author
Owner

@rekram1-node commented on GitHub (Jan 21, 2026):

@vglafirov I think it's an issue in multiple auth plugins, but it is a bug in the plugins themselves (including ours) afaik, since all the refresh logic is self contained to a plugin each one needs to implement proper refresh logic

@rekram1-node commented on GitHub (Jan 21, 2026): @vglafirov I think it's an issue in multiple auth plugins, but it is a bug in the plugins themselves (including ours) afaik, since all the refresh logic is self contained to a plugin each one needs to implement proper refresh logic
Author
Owner

@keramblock commented on GitHub (Jan 22, 2026):

can confirm that error(Error: Token refresh failed: 401) exists for openai codex connector

@keramblock commented on GitHub (Jan 22, 2026): can confirm that error(Error: Token refresh failed: 401) exists for openai codex connector
Author
Owner

@LiJie298 commented on GitHub (Jan 23, 2026):

那么该如何快速解决呢

@LiJie298 commented on GitHub (Jan 23, 2026): 那么该如何快速解决呢
Author
Owner

@keramblock commented on GitHub (Jan 23, 2026):

那么该如何快速解决呢

I don't know Chinese, but if Google Translate is correct, you just need to do this to reissue token:

  1. opencode auth logout
  2. opencode auth login
@keramblock commented on GitHub (Jan 23, 2026): > 那么该如何快速解决呢 I don't know Chinese, but if Google Translate is correct, you just need to do this to reissue token: 1) opencode auth logout 2) opencode auth login
Author
Owner

@SardorbekR commented on GitHub (Jan 26, 2026):

@keramblock cannot find logout button though😅 checked everywhere

@SardorbekR commented on GitHub (Jan 26, 2026): @keramblock cannot find logout button though😅 checked everywhere
Author
Owner

@GreenStage commented on GitHub (Jan 26, 2026):

+1

@GreenStage commented on GitHub (Jan 26, 2026): +1
Author
Owner

@Akswii commented on GitHub (Jan 27, 2026):

type the commands in your terminal @SardorbekR

@Akswii commented on GitHub (Jan 27, 2026): type the commands in your terminal @SardorbekR
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: anomalyco/opencode#6992