mastra-ai/mastra · error
xAI token response missing refresh_token
Error message
xAI token response missing refresh_token
What it means
credentialsFromTokenResponse requires a refresh token: it accepts record.refresh_token if present, otherwise falls back to previousRefreshToken (xAI may not rotate it). If neither exists, credentials could never be renewed, so this error is thrown.
Source
Thrown at mastracode/sdk/src/auth/providers/xai.ts:63
throw new Error(`xAI device authorization returned a non-https verification_uri: ${raw}`);
}
return parsed.toString();
}
function credentialsFromTokenResponse(data: unknown, previousRefreshToken?: string): OAuthCredentials {
const record = (data ?? {}) as Record<string, unknown>;
const access = record.access_token;
if (typeof access !== 'string' || access.length === 0) {
throw new Error('xAI token response missing access_token');
}
// xAI may not rotate the refresh token on refresh; keep the previous one.
const refresh =
typeof record.refresh_token === 'string' && record.refresh_token.length > 0
? record.refresh_token
: previousRefreshToken;
if (!refresh) {
throw new Error('xAI token response missing refresh_token');
}
const expiresIn =
typeof record.expires_in === 'number' && record.expires_in > 0
? record.expires_in
: DEFAULT_TOKEN_EXPIRES_IN_SECONDS;
return {
access,
refresh,
expires: Date.now() + expiresIn * 1000 - REFRESH_SKEW_MS,
};
}
/**
* Serializable pending state for an xAI device-code login. Safe to persist
* (e.g. a `pending jsonb` column) so polling can span HTTP requests.
*/View on GitHub (pinned to 75dd419e61)
Solutions
- Ensure the previous refresh token is passed to refreshXAIToken so the fallback works when xAI does not rotate.
- Log the token response to confirm whether refresh_token was omitted by the provider.
- Re-run the device login to obtain a grant that includes a refresh token.
- Check xAI API docs/changelog for refresh-token issuance changes and upgrade the SDK if needed.
Example fix
// before return credentialsFromTokenResponse(json); // previous refresh token lost // after return credentialsFromTokenResponse(json, storedRefreshToken); // keeps previous token if not rotated
Defensive patterns
Strategy: validation
Validate before calling
// before refreshing, ensure you still hold the previous refresh token to pass through
if (!previousRefreshToken && !responseIncludesRefreshToken) {
await runFullXaiDeviceLogin(); // refresh chain broken; re-auth
} Type guard
function hasRecoverableRefreshToken(data: unknown, previous?: string): boolean {
const r = (data ?? {}) as Record<string, unknown>;
const next = r.refresh_token;
return (typeof next === 'string' && next.length > 0) || (typeof previous === 'string' && previous.length > 0);
} Try / catch
try {
creds = await refreshXAIToken(stored.refresh);
} catch (e) {
if (e instanceof Error && e.message.includes('missing refresh_token')) {
creds = await runXaiDeviceLogin(); // no renewable token; full login
}
} Prevention
- Always pass the previous refresh token to the refresh call so the non-rotation fallback works
- Persist refresh tokens durably before dropping old credentials
- Verify the provider's scope grants include offline access/refresh tokens
- Watch provider changelogs for token-rotation behavior changes
When it happens
Trigger: pollXAITokenOnce or refreshXAIToken gets a response with a valid access_token but no refresh_token, while previousRefreshToken is undefined (initial device login) or also empty.
Common situations: xAI changes token-rotation behavior and omits refresh_token on an initial grant; the caller dropped the previous refresh token before calling refresh; scope/plan differences where the provider does not issue refresh tokens.
Related errors
- xAI token response missing access_token
- xAI device authorization response missing required fields
- Slack OAuth response missing required fields (access_token,
- xAI device authorization returned an invalid verification_ur
- xAI device authorization returned a non-https verification_u
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/644d7d06899fd5ea.
Report an issue: GitHub.