mastra-ai/mastra · error

GitHub OAuth token exchange failed: ${res.status}

Error message

GitHub OAuth token exchange failed: ${res.status}

What it means

The GitHub OAuth web-application flow token exchange (POST to https://github.com/login/oauth/access_token with client_id, client_secret, code, redirect_uri) returned a non-OK HTTP status; the integration surfaces the status code in this error.

Source

Thrown at mastracode/factory/src/integrations/github/integration.ts:1123

    url.searchParams.set('state', state);
    return url.toString();
  }

  /** Exchange an OAuth `code` for a user access token. */
  async exchangeOAuthCode(code: string, redirectUri: string): Promise<string> {
    const res = await fetch('https://github.com/login/oauth/access_token', {
      method: 'POST',
      signal: AbortSignal.timeout(GITHUB_OAUTH_TOKEN_TIMEOUT_MS),
      headers: { 'content-type': 'application/json', accept: 'application/json' },
      body: JSON.stringify({
        client_id: this.#clientId,
        client_secret: this.#clientSecret,
        code,
        redirect_uri: redirectUri,
      }),
    });
    if (!res.ok) {
      throw new Error(`GitHub OAuth token exchange failed: ${res.status}`);
    }
    const data = (await res.json()) as { access_token?: string; error?: string; error_description?: string };
    if (!data.access_token) {
      throw new Error(
        `GitHub OAuth token exchange returned no token: ${data.error_description ?? data.error ?? 'unknown'}`,
      );
    }
    return data.access_token;
  }

  /**
   * The integration's HTTP surface: the `/web/github/*` + `/auth/github/*`
   * Mastra `apiRoutes` (webhook handler, install/OAuth flow, project +
   * worktree + session operations). The factory folds these into the server's
   * `apiRoutes` when the feature is ready. Handlers operate on this instance.
   */
  routes(ctx: IntegrationContext): ApiRoute[] {
    this.#storage = ctx.storage;

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Verify clientId and clientSecret match the GitHub OAuth App
  2. Ensure redirect_uri is byte-identical to the one used in the authorize redirect and registered on the app
  3. Re-run the OAuth flow to obtain a fresh authorization code (codes are single-use and short-lived)
  4. Retry the flow after checking the GitHub status page if the status is 5xx

Example fix

// before
redirect_uri: 'https://app.example.com/auth/github/callback/' // trailing slash differs from authorize step
// after
redirect_uri: 'https://app.example.com/auth/github/callback' // exact match with authorize call
Defensive patterns

Strategy: retry

Validate before calling

if (!clientId || !clientSecret || !code || redirectUri !== expectedRedirectUri) {
  throw new Error('OAuth exchange prerequisites invalid');
}

Try / catch

try {
  const token = await gh.exchangeOAuthCode({ code, redirectUri });
} catch (e) {
  if (e.message.startsWith('GitHub OAuth token exchange failed:')) {
    const status = Number(e.message.match(/(\d+)$/)?.[1]);
    if (status >= 500) retryWithBackoff();
    else restartOauthFlow(); // 4xx: code/credentials/redirect_uri problem — do not retry the same code
  } else throw e;
}

Prevention

When it happens

Trigger: Exchanging an OAuth authorization code for an access token when GitHub responds with 4xx/5xx — most commonly 401 (wrong client_secret), 404 (wrong client_id), or 400 (expired/already-used code or redirect_uri mismatch).

Common situations: Mismatched redirect_uri between the authorize step and the token exchange; authorization code reused or older than ~10 minutes; incorrect/misconfigured GITHUB_CLIENT_SECRET; GitHub outage (5xx).

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/120db0672efc7340. Report an issue: GitHub.