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
- Log the actual response status (add temporary instrumentation or check server logs) to distinguish 401 (token problem) from 5xx (Clerk problem).
- Confirm oauthClientId/secret and fapiUrl all belong to the same Clerk instance; realign them from the Clerk dashboard.
- Retry the callback flow — a fresh access token often resolves transient 401/5xx.
- Check network egress from your deployment (serverless/VPC) to Clerk's FAPI domain and proxy settings.
- 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
- Keep fapiUrl, secret key and OAuth credentials from the same Clerk instance.
- Verify server egress to Clerk's FAPI domain from your deployment environment.
- Retry the callback flow once with a fresh token before failing.
- Subscribe to Clerk status updates for incident awareness.
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
- Token exchange failed: ${error}
- Redirect URI is required for SSO login
- Google service account token request failed (${response.stat
- Token exchange failed: ${error}
- Failed to fetch user info from Auth0
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/9490770cf9e66547.
Report an issue: GitHub.