mastra-ai/mastra · error

Token exchange failed: ${error}

Error message

Token exchange failed: ${error}

What it means

During the SSO OAuth callback, the provider exchanges the authorization code for tokens at Auth0's /oauth/token endpoint. This error wraps the raw error body returned by Auth0 when the HTTP response is not ok (e.g. 400/401/403), so the upstream message (invalid code, bad client credentials, redirect mismatch, etc.) is surfaced verbatim. It is a network/API-response failure, not a local validation.

Source

Thrown at auth/auth0/src/index.ts:546

      const { redirectUri } = verifyStateToken(signedState, self.cookiePassword);

      // Exchange code for tokens
      const tokenResponse = await fetch(`https://${self.domain}/oauth/token`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          grant_type: 'authorization_code',
          client_id: self.clientId,
          client_secret: self.clientSecret,
          code,
          redirect_uri: redirectUri,
        }),
        signal: AbortSignal.timeout(10_000), // 10 second timeout
      });

      if (!tokenResponse.ok) {
        const error = await tokenResponse.text();
        throw new Error(`Token exchange failed: ${error}`);
      }

      const tokens = (await tokenResponse.json()) as {
        access_token: string;
        id_token?: string;
        refresh_token?: string;
        expires_in: number;
        token_type: string;
      };

      // Get user info from ID token or userinfo endpoint
      let user: EEUser;
      if (tokens.id_token) {
        try {
          const JWKS = createRemoteJWKSet(new URL(`https://${self.domain}/.well-known/jwks.json`));
          const { payload } = await jwtVerify(tokens.id_token, JWKS, {
            issuer: `https://${self.domain}/`,
            audience: self.clientId!, // Validate token was issued for this client

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Log the full error body included in the message — it names the exact OAuth error (e.g. invalid_grant, invalid_client)
  2. Check that the redirect_uri used in the token exchange exactly matches the one used in getLoginUrl and is registered in the Auth0 application's Allowed Callback URLs
  3. Verify clientId/clientSecret are current and not rotated/expired in Auth0 dashboard
  4. Redirect the user back to a fresh login flow on invalid_grant (codes are single-use and expire in ~10 minutes)
  5. Confirm the provider's domain points at the correct Auth0 tenant

Example fix

// before
const { originalState, redirectUri } = authServer.redirectUri(state);
const tokens = await exchangeCode(code); // throws raw
// after
try {
  const tokens = await exchangeCode(code);
} catch (e) {
  if (String(e.message).includes('invalid_grant')) {
    return Response.redirect(provider.getLoginUrl(callbackUri, newState()), 302);
  }
  throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Cannot be pre-validated locally; but ensure before the flow:
// 1. redirectUri identical in /authorize and /token
// 2. clientId/clientSecret current
// 3. domain matches the Auth0 tenant that issued the code

Try / catch

try {
  const tokens = await exchangeAuthorizationCode(code);
} catch (e) {
  const msg = e instanceof Error ? e.message : String(e);
  if (msg.includes('invalid_grant') || msg.includes('Token exchange failed')) {
    // code expired/reused — restart login
    return redirectTo(provider.getLoginUrl(callbackUri, newState()));
  }
  if (msg.includes('invalid_client')) {
    throw new Error('Auth0 client credentials invalid — check clientId/clientSecret', { cause: e });
  }
  throw e;
}

Prevention

When it happens

Trigger: The token POST inside the SSO callback handler returns a non-ok status — e.g. the authorization code was already used or expired (invalid_grant), client_id/client_secret are wrong (invalid_client), or the redirect_uri in the exchange doesn't exactly match the one used in the login URL or Auth0 app settings.

Common situations: User refreshing the callback page causing code reuse; mismatched redirect URI between /authorize and /token calls; wrong client secret after rotating credentials in Auth0; Auth0 tenant/domain misconfiguration; network issues causing truncated responses.

Related errors


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