can1357/oh-my-pi · error · AIError.OAuthError
${label} missing access_token
Error message
${label} missing access_token What it means
Thrown by parseXAITokenResponse when the token payload is a JSON object but access_token is missing or not a non-empty string. Without an access token the library cannot construct OAuthCredentials, so the exchange is treated as failed. The message's label tells you which exchange (initial device poll or refresh) produced the bad body.
Source
Thrown at packages/ai/src/registry/oauth/xai-oauth.ts:336
verificationUriComplete,
expiresInSeconds,
intervalSeconds,
};
}
function parseXAITokenResponse(payload: unknown, label: string, refreshTokenFallback?: string): OAuthCredentials {
if (!isRecord(payload)) {
throw new AIError.OAuthError(`${label} was not a JSON object`, {
kind: "validation",
provider: "xai",
});
}
const accessToken = typeof payload.access_token === "string" ? payload.access_token : "";
const responseRefreshToken = typeof payload.refresh_token === "string" ? payload.refresh_token : "";
const refreshToken = responseRefreshToken || refreshTokenFallback || "";
const expiresInSeconds = payload.expires_in;
if (!accessToken) {
throw new AIError.OAuthError(`${label} missing access_token`, {
kind: "validation",
provider: "xai",
});
}
if (!refreshToken) {
throw new AIError.OAuthError(`${label} missing refresh_token`, {
kind: "validation",
provider: "xai",
});
}
if (typeof expiresInSeconds !== "number" || !Number.isFinite(expiresInSeconds)) {
throw new AIError.OAuthError(`${label} missing expires_in`, {
kind: "validation",
provider: "xai",
});
}
return {
access: accessToken,View on GitHub (pinned to 9690622007)
Solutions
- Read the rest of the payload (curl the token endpoint) — an `error` field usually explains the rejection (invalid_grant, expired_token, slow_down).
- Re-run the device login flow (`omp` login) to obtain a fresh device code and tokens.
- If refreshing, delete the stored xAI credentials and re-authenticate — the refresh token is likely invalid.
- Update the ai package if xAI renamed access_token in a newer token response format.
Example fix
// before: assuming a 200 means success
if (res.ok) {
const body = await res.json();
useToken(body.access_token); // may be undefined
}
// after: check the OAuth error field before consuming the token
const body = await res.json();
if (body.error) {
throw new Error(`token endpoint error: ${body.error}`);
}
if (typeof body.access_token !== "string" || !body.access_token) {
throw new Error("token response missing access_token");
}
useToken(body.access_token); Defensive patterns
Strategy: try-catch
Validate before calling
// preflight: reject token bodies that carry an OAuth error or lack access_token
const body: Record<string, unknown> = await res.json();
if (typeof body.error === "string") {
throw new Error(`xAI token exchange rejected: ${body.error}`);
}
if (typeof body.access_token !== "string" || body.access_token === "") {
throw new Error("xAI token response lacks access_token");
} Type guard
function hasAccessToken(v: unknown): v is { access_token: string } & Record<string, unknown> {
return typeof v === "object" && v !== null &&
typeof (v as Record<string, unknown>).access_token === "string" &&
(v as { access_token: string }).access_token !== "";
} Try / catch
try {
await xaiProvider.credentials();
} catch (err) {
if (err instanceof AIError.OAuthError && err.message.includes("missing access_token")) {
// stale/revoked grant — clear stored credentials and re-run device login
await deleteStoredXAICredentials();
return xaiProvider.credentials();
}
throw err;
} Prevention
- Clear stale xAI credentials before re-authenticating after long inactivity.
- Check the token body's `error` field first — most missing access_token cases are invalid_grant/expired_token.
- Complete device authorization in the browser before the code expires to avoid denied exchanges.
- Monitor xAI status pages during 5xx incidents before blaming stored credentials.
When it happens
Trigger: pollXAIDeviceToken gets HTTP 200 with a body lacking a string access_token (e.g. only an error field, or an empty token), or the credentials refresh path receives a payload without access_token; parseXAITokenResponse throws '<label> missing access_token'.
Common situations: xAI returning an OAuth error JSON (error=invalid_grant etc.) with HTTP 200; expired/revoked device code; refresh token revoked server-side; clock or account issues on the xAI side.
Related errors
- xAI device-code response missing or invalid required fields.
- ${label} missing refresh_token
- xAI device-code response was not a JSON object.
- ${label} missing expires_in
- xAI device-code request failed: ${error instanceof Error ? e
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/f1a3f0209a796f70.
Report an issue: GitHub.