can1357/oh-my-pi · error · AIError.OAuthError
xAI device-code response missing or invalid required fields.
Error message
xAI device-code response missing or invalid required fields.
What it means
Thrown by parseXAIDeviceAuthorization when the device-code response is a JSON object but one or more required fields are absent or invalid: device_code, user_code, verification_uri, verification_uri_complete must be non-empty strings, and expires_in/interval must be positive finite numbers. The library needs all of these to run the device login flow, so a partially-formed response is rejected wholesale.
Source
Thrown at packages/ai/src/registry/oauth/xai-oauth.ts:307
const userCode = typeof payload.user_code === "string" ? payload.user_code.trim() : "";
const verificationUri = typeof payload.verification_uri === "string" ? payload.verification_uri.trim() : "";
const verificationUriComplete =
typeof payload.verification_uri_complete === "string" ? payload.verification_uri_complete.trim() : "";
const expiresInSeconds = payload.expires_in;
const intervalSeconds = payload.interval;
if (
!deviceCode ||
!userCode ||
!verificationUri ||
!verificationUriComplete ||
typeof expiresInSeconds !== "number" ||
!Number.isFinite(expiresInSeconds) ||
expiresInSeconds <= 0 ||
typeof intervalSeconds !== "number" ||
!Number.isFinite(intervalSeconds) ||
intervalSeconds <= 0
) {
throw new AIError.OAuthError("xAI device-code response missing or invalid required fields.", {
kind: "validation",
provider: "xai",
});
}
validateXAIEndpoint(verificationUri, "verification_uri");
validateXAIEndpoint(verificationUriComplete, "verification_uri_complete");
return {
deviceCode,
userCode,
verificationUriComplete,
expiresInSeconds,
intervalSeconds,
};
}
function parseXAITokenResponse(payload: unknown, label: string, refreshTokenFallback?: string): OAuthCredentials {
if (!isRecord(payload)) {View on GitHub (pinned to 9690622007)
Solutions
- Log/curl the raw device-code response to see which field is missing or malformed.
- Retry the login — an error payload with HTTP 200 usually means a transient xAI-side condition.
- Check for proxy/VPN interference that strips or renames response fields.
- Update @oh-my-pi/pi-ai — if xAI changed the device-flow field names, a newer package version has the adjusted parser.
Example fix
// before: manually extracting fields from a response you did not validate
const { device_code } = body;
startPolling(device_code);
// after: only proceed when the fields the flow requires are present and typed
if (typeof body.device_code !== "string" || !body.device_code ||
typeof body.expires_in !== "number" || body.expires_in <= 0) {
throw new Error("xAI device-code response missing required fields");
}
startPolling(body.device_code); Defensive patterns
Strategy: validation
Validate before calling
// preflight: verify the device-code response carries the fields the flow needs
const body: Record<string, unknown> = await res.json();
const required = ["device_code", "user_code", "verification_uri", "verification_uri_complete"];
const missing = required.filter((k) => typeof body[k] !== "string" || !(body[k] as string).trim());
if (missing.length > 0 || typeof body.expires_in !== "number" || body.expires_in <= 0 ||
typeof body.interval !== "number" || body.interval <= 0) {
console.error("xAI device-code response invalid; fields missing/invalid:", missing, body);
} Type guard
function isXAIDeviceAuthorization(v: unknown): v is {
device_code: string; user_code: string; verification_uri: string;
verification_uri_complete: string; expires_in: number; interval: number;
} {
if (typeof v !== "object" || v === null) return false;
const b = v as Record<string, unknown>;
return typeof b.device_code === "string" && b.device_code.trim() !== "" &&
typeof b.user_code === "string" && b.user_code.trim() !== "" &&
typeof b.verification_uri === "string" && b.verification_uri.trim() !== "" &&
typeof b.verification_uri_complete === "string" && b.verification_uri_complete.trim() !== "" &&
typeof b.expires_in === "number" && Number.isFinite(b.expires_in) && b.expires_in > 0 &&
typeof b.interval === "number" && Number.isFinite(b.interval) && b.interval > 0;
} Try / catch
try {
const auth = await xaiProvider.device();
openBrowser(auth.verificationUriComplete);
} catch (err) {
if (err instanceof AIError.OAuthError && err.kind === "validation" && err.message.includes("missing or invalid required fields")) {
logger.error("xAI device-code payload incomplete — dump raw response and retry", {});
} else {
throw err;
}
} Prevention
- Log the raw device-code response once per environment so a schema change is caught immediately.
- Avoid middleboxes that strip unknown JSON fields from API responses.
- Treat an HTTP 200 body containing an `error` field as a failure signal before parsing fields.
- Update the ai package when xAI revises the device-flow field names.
When it happens
Trigger: requestXAIDeviceAuthorization receives a 200 JSON object from the xAI device-code endpoint that is missing device_code/user_code/verification_uri/verification_uri_complete, has empty or whitespace-only string values, or has expires_in/interval that are non-numeric, non-finite, or <= 0.
Common situations: xAI returns an error-style payload (e.g. {"error":"access_denied"}) with HTTP 200; a proxy truncates or rewrites fields; xAI renames a field in a API revision; a custom fetchImpl injects defaults that violate the contract.
Related errors
- xAI device-code response was not a JSON object.
- ${label} missing access_token
- ${label} missing refresh_token
- ${label} missing expires_in
- ${label} was not a JSON object
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/ac694d51a5b52827.
Report an issue: GitHub.