can1357/oh-my-pi · error · AIError.OAuthError
GitLab OAuth token exchange failed: ${response.status} ${awa
Error message
GitLab OAuth token exchange failed: ${response.status} ${await response.text()} What it means
exchangeToken finishes the browser OAuth flow by POSTing the authorization code + PKCE verifier to https://gitlab.com/oauth/token. If GitLab answers non-2xx, this OAuthError is thrown with the status and response body in the message. It means the one-time authorization code could not be converted into tokens.
Source
Thrown at packages/ai/src/registry/oauth/gitlab-duo.ts:164
"Personal Access Token via GITLAB_TOKEN.",
};
}
override async exchangeToken(code: string, _state: string, redirectUri: string): Promise<OAuthCredentials> {
const response = await this.#fetch(`${GITLAB_COM_URL}/oauth/token`, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
client_id: this.#clientId,
grant_type: "authorization_code",
code,
code_verifier: this.#pkce.verifier,
redirect_uri: redirectUri,
}).toString(),
});
if (!response.ok) {
throw new AIError.OAuthError(
`GitLab OAuth token exchange failed: ${response.status} ${await response.text()}`,
{
kind: "token-exchange",
provider: "gitlab-duo",
status: response.status,
},
);
}
clearGitLabDuoDirectAccessCache();
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
- Ensure GITLAB_REDIRECT_URI exactly matches a redirect URI registered on your GitLab OAuth application (strict string match).
- Restart the login flow to obtain a fresh authorization code — codes are single-use and expire within ~10 minutes.
- Register your own GitLab OAuth app and set both GITLAB_CLIENT_ID and GITLAB_REDIRECT_URI if the bundled credentials fail.
- Bypass OAuth with GITLAB_TOKEN (Personal Access Token) if browser login is not viable.
- Read the status/body in the message: 400 invalid_grant usually means code reuse/expiry; 401 invalid_client means wrong client id.
Example fix
// before: client id overridden, redirect not updated GITLAB_CLIENT_ID=my-new-app-id GITLAB_REDIRECT_URI=http://localhost:9999/cb // after: pair must exactly match the GitLab app registration GITLAB_CLIENT_ID=my-new-app-id GITLAB_REDIRECT_URI=http://localhost:8080/callback
Defensive patterns
Strategy: retry
Validate before calling
const redirectUri = process.env.GITLAB_REDIRECT_URI?.trim();
const clientId = process.env.GITLAB_CLIENT_ID?.trim();
if (Boolean(clientId) !== Boolean(redirectUri)) {
console.warn("GITLAB_CLIENT_ID and GITLAB_REDIRECT_URI should be set together and match the GitLab app registration");
} Try / catch
let tokens: OAuthCredentials;
try {
tokens = await loginGitLabDuo(callbacks);
} catch (err) {
if (err?.kind === "token-exchange" && err.status === 400) {
// code expired/consumed or redirect mismatch — restart the flow with a fresh code
tokens = await loginGitLabDuo(callbacks);
} else {
throw err;
}
} Prevention
- Never retry with the same authorization code — codes are single-use and short-lived.
- Keep GITLAB_REDIRECT_URI byte-identical to the URI registered on the GitLab application.
- Set GITLAB_CLIENT_ID and GITLAB_REDIRECT_URI as a pair; changing one without the other breaks the exchange.
- Complete the browser step promptly — codes expire in minutes.
When it happens
Trigger: POST /oauth/token with grant_type=authorization_code returns !ok: redirect_uri mismatch (GITLAB_REDIRECT_URI not exactly matching the GitLab app registration), expired or already-used authorization code, wrong PKCE code_verifier, invalid client_id, or revoked/invalidated code.
Common situations: The documented 'The redirect URI included is not valid' failure when the bundled client id's registered redirect list changed (issue #2424); retried logins reusing a consumed code; clock skew expiring the code; switching GITLAB_CLIENT_ID without updating GITLAB_REDIRECT_URI.
Related errors
- GitLab Duo Workflow OAuth refresh failed: ${response.status}
- GitLab OAuth refresh failed: ${response.status} ${await resp
- Token exchange failed: ${response.status} ${errorText}
- Failed to initiate device authorization: ${initiateResponse.
- Invalid GITLAB_REDIRECT_URI: ${raw}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/3a3bf57e8af36117.
Report an issue: GitHub.