can1357/oh-my-pi · error · AIError.OAuthError

GitLab Duo Workflow OAuth refresh failed: ${response.status}

Error message

GitLab Duo Workflow OAuth refresh failed: ${response.status} ${await response.text()}

What it means

refreshGitLabDuoWorkflowToken POSTs a refresh_token grant to https://gitlab.com/oauth/token using GitLab's VS Code Workflow client id and the vscode:// redirect URI. When GitLab responds with a non-2xx status, this OAuthError is thrown with the HTTP status and the raw response body embedded in the message. It means the stored refresh token could no longer be exchanged for a new access token.

Source

Thrown at packages/ai/src/registry/oauth/gitlab-duo-workflow.ts:128

}

export async function refreshGitLabDuoWorkflowToken(
	credentials: OAuthCredentials,
	fetchImpl: FetchImpl = fetch,
): Promise<OAuthCredentials> {
	const response = await fetchImpl(`${GITLAB_COM_URL}/oauth/token`, {
		method: "POST",
		headers: { "Content-Type": "application/x-www-form-urlencoded" },
		body: new URLSearchParams({
			client_id: GITLAB_DUO_WORKFLOW_OAUTH_CLIENT_ID,
			redirect_uri: GITLAB_DUO_WORKFLOW_OAUTH_REDIRECT_URI,
			grant_type: "refresh_token",
			refresh_token: credentials.refresh,
		}).toString(),
	});

	if (!response.ok) {
		throw new AIError.OAuthError(
			`GitLab Duo Workflow OAuth refresh failed: ${response.status} ${await response.text()}`,
			{
				kind: "token-refresh",
				provider: "gitlab-duo-workflow",
				status: response.status,
			},
		);
	}

	return mapTokenResponse(
		(await response.json()) as {
			access_token?: string;
			refresh_token?: string;
			expires_in?: number;
			created_at?: number;
		},
	);
}

View on GitHub (pinned to 9690622007)

Solutions

  1. Re-run the GitLab Duo Workflow OAuth login (loginGitLabDuoWorkflow) to obtain fresh access/refresh tokens and replace the stored credentials.
  2. Check GitLab Profile > Applications / Active Sessions and re-authorize if the application or token was revoked.
  3. Verify the stored credentials object actually contains a non-empty refresh token; re-login if it is empty or malformed.
  4. If the status is 5xx, retry after a short delay — it may be a transient GitLab outage.
  5. As a fallback, authenticate via a GitLab Personal Access Token (GITLAB_TOKEN) if the provider supports it.

Example fix

// before: assuming the stored refresh token is forever valid
await refreshGitLabDuoWorkflowToken(credentials);

// after: catch the OAuthError and fall back to interactive re-login
try {
  credentials = await refreshGitLabDuoWorkflowToken(credentials);
} catch (err) {
  if (err.kind === "token-refresh" && err.status === 401) {
    credentials = await loginGitLabDuoWorkflow(callbacks); // re-authenticate
  } else {
    throw err;
  }
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (!credentials?.refresh || typeof credentials.refresh !== "string") {
  throw new Error("cannot refresh: missing refresh token; re-run loginGitLabDuoWorkflow");
}

Type guard

function hasRefreshToken(c: unknown): c is { refresh: string } {
  return typeof c === "object" && c !== null && typeof (c as { refresh?: unknown }).refresh === "string" && (c as { refresh: string }).refresh.length > 0;
}

Try / catch

try {
  credentials = await refreshGitLabDuoWorkflowToken(credentials);
} catch (err) {
  const status = err?.status;
  if (status === 401 || status === 400) {
    credentials = await loginGitLabDuoWorkflow(callbacks); // token dead, re-auth
  } else if (status >= 500) {
    await Bun.sleep(2000); // transient GitLab outage, retry once
    credentials = await refreshGitLabDuoWorkflowToken(credentials);
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: The refresh request to /oauth/token returns !response.ok: expired or revoked refresh_token, token revoked by a GitLab password change or account session revocation, malformed credentials.refresh (empty/missing), or GitLab returning 4xx/5xx (invalid_client, invalid_grant, server errors).

Common situations: Long-lived sessions where the refresh token expired (GitLab refresh tokens can expire after inactivity); user revoked the app in GitLab Profile > Applications; admin disabled Duo Workflow; stored credentials were truncated by a config tool; transient GitLab 5xx outages during automatic token refresh.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/8574368cdd4ee692. Report an issue: GitHub.