mastra-ai/mastra · error

Failed to fetch user info from Clerk

Error message

Failed to fetch user info from Clerk

What it means

With a successfully obtained access token, the provider calls Clerk's `/oauth/userinfo` endpoint (on the FAPI URL) to fetch the user's profile (sub, email, name). If that HTTP response is not ok — token rejected, endpoint unreachable/5xx, scopes missing — the library throws this generic error. Unlike the token-exchange error, the upstream body is discarded, so the message doesn't include the server's reason.

Source

Thrown at auth/clerk/src/index.ts:594

      // Get user info — try ID token first, fall back to userinfo endpoint
      let user: EEUser;
      if (tokens.id_token) {
        const payload = await verifyJwks(tokens.id_token, self.jwksUri);
        user = {
          id: payload.sub!,
          email: (payload.email as string) ?? undefined,
          name: (payload.name as string) ?? undefined,
          avatarUrl: (payload.picture as string) ?? undefined,
        };
      } else {
        const userInfoResponse = await fetch(`${self.fapiUrl}/oauth/userinfo`, {
          headers: { Authorization: `Bearer ${tokens.access_token}` },
          signal: AbortSignal.timeout(10_000), // 10 second timeout
        });

        if (!userInfoResponse.ok) {
          throw new Error('Failed to fetch user info from Clerk');
        }

        const userInfo = (await userInfoResponse.json()) as {
          sub: string;
          email?: string;
          name?: string;
          picture?: string;
        };
        user = {
          id: userInfo.sub,
          email: userInfo.email,
          name: userInfo.name,
          avatarUrl: userInfo.picture,
        };
      }

      // Try to enrich user with full Clerk data
      try {

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Log the actual response status (add temporary instrumentation or check server logs) to distinguish 401 (token problem) from 5xx (Clerk problem).
  2. Confirm oauthClientId/secret and fapiUrl all belong to the same Clerk instance; realign them from the Clerk dashboard.
  3. Retry the callback flow — a fresh access token often resolves transient 401/5xx.
  4. Check network egress from your deployment (serverless/VPC) to Clerk's FAPI domain and proxy settings.
  5. Check Clerk status page for an ongoing incident.

Example fix

// before: raw failure to user
await provider.handleCallback(url);
// after
try {
  await provider.handleCallback(url);
} catch (e) {
  if ((e as Error).message === 'Failed to fetch user info from Clerk') {
    return res.redirect('/auth/sso/login'); // re-run flow with fresh token
  }
  throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

async function canReachClerkUserInfo(fapiUrl: string, token: string): Promise<boolean> {
  try {
    const r = await fetch(`${fapiUrl}/oauth/userinfo`, {
      headers: { Authorization: `Bearer ${token}` },
      signal: AbortSignal.timeout(10_000),
    });
    return r.ok;
  } catch {
    return false;
  }
}

Try / catch

try {
  await provider.handleCallback(req.url);
} catch (e) {
  if ((e as Error).message === 'Failed to fetch user info from Clerk') {
    logger.error('Clerk userinfo fetch failed; check token validity, FAPI URL and egress network');
    return restartLoginFlow();
  }
  throw e;
}

Prevention

When it happens

Trigger: SSO callback reaches the userinfo fetch and `userInfoResponse.ok` is false: access_token rejected by Clerk (revoked/wrong instance), Clerk FAPI returning 5xx, proxy blocking the request, or the 10s AbortSignal.timeout firing (which also manifests as a failed fetch).

Common situations: Secret keys from one Clerk instance paired with a FAPI URL from another; Clerk incident/degradation; corporate egress proxy or firewall blocking the call from a serverless function; clock/auth issues causing immediate token invalidation.

Related errors


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