{"record":{"id":"348a1cb432925ce7","repo":"can1357/oh-my-pi","slug":"xai-device-code-response-returned-invalid-json","errorCode":null,"errorMessage":"xAI device-code response returned invalid JSON: ${error instanceof Error ? error.message : String(error)}","messagePattern":"xAI device-code response returned invalid JSON: (.+?)","errorType":"validation","errorClass":"AIError.OAuthError","httpStatus":null,"severity":"error","filePath":"packages/ai/src/registry/oauth/xai-oauth.ts","lineNumber":405,"sourceCode":"\tif (!response.ok) {\n\t\tlet detail = \"\";\n\t\ttry {\n\t\t\tdetail = (await response.text()).trim();\n\t\t} catch {\n\t\t\t// Ignore body-read failures; the status code is the diagnostic.\n\t\t}\n\t\tthrow new AIError.OAuthError(`xAI device-code request failed: ${response.status}${detail ? ` ${detail}` : \"\"}`, {\n\t\t\tkind: \"device-auth\",\n\t\t\tprovider: \"xai\",\n\t\t\tstatus: response.status,\n\t\t});\n\t}\n\n\tlet payload: unknown;\n\ttry {\n\t\tpayload = await response.json();\n\t} catch (error) {\n\t\tthrow new AIError.OAuthError(\n\t\t\t`xAI device-code response returned invalid JSON: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t{ kind: \"validation\", provider: \"xai\", cause: error },\n\t\t);\n\t}\n\treturn parseXAIDeviceAuthorization(payload);\n}\n\nasync function pollXAIDeviceToken(\n\ttokenEndpoint: string,\n\tdeviceCode: string,\n\tfetchImpl: FetchImpl,\n\tsignal?: AbortSignal,\n): Promise<OAuthDeviceCodePollResult<OAuthCredentials>> {\n\tlet response: Response;\n\ttry {\n\t\tconst timeoutSignal = AbortSignal.timeout(TOKEN_REQUEST_TIMEOUT_MS);\n\t\tresponse = await fetchImpl(tokenEndpoint, {\n\t\t\tmethod: \"POST\",","sourceCodeStart":387,"sourceCodeEnd":423,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/ai/src/registry/oauth/xai-oauth.ts#L387-L423","documentation":"Thrown by requestXAIDeviceAuthorization when response.json() fails — the device-code endpoint returned a 2xx status but a body that is not valid JSON (HTML error page, empty body, truncated response). The parse error is attached as `cause`. A success status with an unparseable body usually indicates an intermediary or a non-JSON response contract, not an xAI application error.","triggerScenarios":"GET/POST to the device-code endpoint returns 200 with HTML (captive portal, proxy block page), an empty body, or truncated/invalid JSON; response.json() throws SyntaxError ('Unexpected token < in JSON', 'Unexpected end of JSON input').","commonSituations":"Captive Wi-Fi portals intercepting HTTPS; corporate SSL-inspection proxies injecting error pages; CDN edge errors served with 200; DNS hijacking; response body cut off by a flaky connection.","solutions":["Look at `error.cause` and curl the device-code endpoint from the same machine to see the raw non-JSON body.","Log into the network (captive portal) or disable the intercepting proxy/VPN — HTML block pages are the top cause.","Retry the login after network conditions change; truncated responses are often transient.","If it reproduces consistently, capture the body and report/update the ai package in case xAI changed its response format."],"exampleFix":"// before: blindly parsing\nconst body = JSON.parse(await res.text());\n// after: guard the parse and surface the raw body for diagnosis\nconst text = await res.text();\nlet body: unknown;\ntry {\n  body = JSON.parse(text);\n} catch (err) {\n  throw new Error(`xAI device-code endpoint returned non-JSON: ${text.slice(0, 200)}`, { cause: err });\n}","handlingStrategy":"try-catch","validationCode":"// preflight: probe that the device-code URL returns JSON, not an HTML block page\nconst probe = await fetch(xaiDeviceCodeUrl, { method: \"POST\", headers: { Accept: \"application/json\" } });\nconst ct = probe.headers.get(\"content-type\") ?? \"\";\nif (!ct.includes(\"json\")) {\n  throw new Error(`xAI device-code endpoint returned non-JSON content-type: ${ct} — captive portal/proxy likely`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  await xaiProvider.device();\n} catch (err) {\n  if (err instanceof AIError.OAuthError && err.kind === \"validation\" && err.message.includes(\"invalid JSON\")) {\n    logger.error(\"xAI device-code response was not JSON — inspect err.cause and check for proxy/captive portal\", { cause: err.cause });\n    // advise user: log into captive portal or bypass SSL-inspecting proxy\n  } else {\n    throw err;\n  }\n}","preventionTips":["Complete captive-portal sign-in before running device login on public Wi-Fi.","Bypass or properly configure SSL-inspection proxies for xAI domains.","Check `err.cause` — the JSON SyntaxError plus the content-type tells you what was actually served.","Retry once after network changes; truncated bodies are often transient."],"tags":["json","oauth","xai","device-flow","proxy","captive-portal"],"backgroundTag":"invalid-json-response","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}