{"record":{"id":"ac694d51a5b52827","repo":"can1357/oh-my-pi","slug":"xai-device-code-response-missing-or-invalid-requir","errorCode":null,"errorMessage":"xAI device-code response missing or invalid required fields.","messagePattern":"xAI device-code response missing or invalid required fields\\.","errorType":"validation","errorClass":"AIError.OAuthError","httpStatus":null,"severity":"error","filePath":"packages/ai/src/registry/oauth/xai-oauth.ts","lineNumber":307,"sourceCode":"\tconst userCode = typeof payload.user_code === \"string\" ? payload.user_code.trim() : \"\";\n\tconst verificationUri = typeof payload.verification_uri === \"string\" ? payload.verification_uri.trim() : \"\";\n\tconst verificationUriComplete =\n\t\ttypeof payload.verification_uri_complete === \"string\" ? payload.verification_uri_complete.trim() : \"\";\n\tconst expiresInSeconds = payload.expires_in;\n\tconst intervalSeconds = payload.interval;\n\tif (\n\t\t!deviceCode ||\n\t\t!userCode ||\n\t\t!verificationUri ||\n\t\t!verificationUriComplete ||\n\t\ttypeof expiresInSeconds !== \"number\" ||\n\t\t!Number.isFinite(expiresInSeconds) ||\n\t\texpiresInSeconds <= 0 ||\n\t\ttypeof intervalSeconds !== \"number\" ||\n\t\t!Number.isFinite(intervalSeconds) ||\n\t\tintervalSeconds <= 0\n\t) {\n\t\tthrow new AIError.OAuthError(\"xAI device-code response missing or invalid required fields.\", {\n\t\t\tkind: \"validation\",\n\t\t\tprovider: \"xai\",\n\t\t});\n\t}\n\n\tvalidateXAIEndpoint(verificationUri, \"verification_uri\");\n\tvalidateXAIEndpoint(verificationUriComplete, \"verification_uri_complete\");\n\treturn {\n\t\tdeviceCode,\n\t\tuserCode,\n\t\tverificationUriComplete,\n\t\texpiresInSeconds,\n\t\tintervalSeconds,\n\t};\n}\n\nfunction parseXAITokenResponse(payload: unknown, label: string, refreshTokenFallback?: string): OAuthCredentials {\n\tif (!isRecord(payload)) {","sourceCodeStart":289,"sourceCodeEnd":325,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/ai/src/registry/oauth/xai-oauth.ts#L289-L325","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: manually extracting fields from a response you did not validate\nconst { device_code } = body;\nstartPolling(device_code);\n// after: only proceed when the fields the flow requires are present and typed\nif (typeof body.device_code !== \"string\" || !body.device_code ||\n    typeof body.expires_in !== \"number\" || body.expires_in <= 0) {\n  throw new Error(\"xAI device-code response missing required fields\");\n}\nstartPolling(body.device_code);","handlingStrategy":"validation","validationCode":"// preflight: verify the device-code response carries the fields the flow needs\nconst body: Record<string, unknown> = await res.json();\nconst required = [\"device_code\", \"user_code\", \"verification_uri\", \"verification_uri_complete\"];\nconst missing = required.filter((k) => typeof body[k] !== \"string\" || !(body[k] as string).trim());\nif (missing.length > 0 || typeof body.expires_in !== \"number\" || body.expires_in <= 0 ||\n    typeof body.interval !== \"number\" || body.interval <= 0) {\n  console.error(\"xAI device-code response invalid; fields missing/invalid:\", missing, body);\n}","typeGuard":"function isXAIDeviceAuthorization(v: unknown): v is {\n  device_code: string; user_code: string; verification_uri: string;\n  verification_uri_complete: string; expires_in: number; interval: number;\n} {\n  if (typeof v !== \"object\" || v === null) return false;\n  const b = v as Record<string, unknown>;\n  return typeof b.device_code === \"string\" && b.device_code.trim() !== \"\" &&\n    typeof b.user_code === \"string\" && b.user_code.trim() !== \"\" &&\n    typeof b.verification_uri === \"string\" && b.verification_uri.trim() !== \"\" &&\n    typeof b.verification_uri_complete === \"string\" && b.verification_uri_complete.trim() !== \"\" &&\n    typeof b.expires_in === \"number\" && Number.isFinite(b.expires_in) && b.expires_in > 0 &&\n    typeof b.interval === \"number\" && Number.isFinite(b.interval) && b.interval > 0;\n}","tryCatchPattern":"try {\n  const auth = await xaiProvider.device();\n  openBrowser(auth.verificationUriComplete);\n} catch (err) {\n  if (err instanceof AIError.OAuthError && err.kind === \"validation\" && err.message.includes(\"missing or invalid required fields\")) {\n    logger.error(\"xAI device-code payload incomplete — dump raw response and retry\", {});\n  } else {\n    throw err;\n  }\n}","preventionTips":["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."],"tags":["oauth","xai","device-flow","missing-field","response-validation"],"backgroundTag":"oauth-response-shape-mismatch","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}