{"record":{"id":"b215acad38e41867","repo":"chatboxai/chatbox","slug":"response-status-response-statustext-text","errorCode":null,"errorMessage":"${response.status} ${response.statusText}: ${text}","messagePattern":"\\$\\{response\\.status\\} \\$\\{response\\.statusText\\}: \\$\\{text\\}","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/oauth/providers/github-copilot.ts","lineNumber":23,"sourceCode":"const CLIENT_ID = decode('T3YyM2xpOHR3ZVF3Nm9kV1FlYno=')\nconst GITHUB_DOMAIN = 'github.com'\n\n// Pending device flow state\nlet pendingDeviceCode: string | null = null\nlet pendingInterval = 5\n\nfunction getUrls(domain: string) {\n  return {\n    deviceCodeUrl: `https://${domain}/login/device/code`,\n    accessTokenUrl: `https://${domain}/login/oauth/access_token`,\n  }\n}\n\nasync function fetchJson(url: string, init: RequestInit): Promise<unknown> {\n  const response = await fetch(url, init)\n  if (!response.ok) {\n    const text = await response.text()\n    throw new Error(`${response.status} ${response.statusText}: ${text}`)\n  }\n  return response.json()\n}\n\nexport const githubCopilotOAuthProvider: DeviceCodeOAuthProvider = {\n  kind: 'device-code',\n  providerId: 'github-copilot',\n  name: 'GitHub Copilot',\n\n  async startDeviceFlow() {\n    const urls = getUrls(GITHUB_DOMAIN)\n    const data = (await fetchJson(urls.deviceCodeUrl, {\n      method: 'POST',\n      headers: {\n        Accept: 'application/json',\n        'Content-Type': 'application/json',\n      },\n      body: JSON.stringify({","sourceCodeStart":5,"sourceCodeEnd":41,"githubUrl":"https://github.com/chatboxai/chatbox/blob/81571269addb6bafb589a920b2883f1e1e084fd1/src/main/oauth/providers/github-copilot.ts#L5-L41","documentation":"Generic HTTP failure thrown by the internal fetchJson() helper used by both startDeviceFlow() (POST /login/device/code) and waitForToken() (POST /login/oauth/access_token). The message combines status, statusText, and body so the underlying GitHub response (e.g. 404, 422 JSON, rate-limit text) is visible. Because GitHub returns pending/slow_down as HTTP 200 with an error field, a throw from fetchJson always indicates a genuine HTTP-level failure, not the expected polling states.","triggerScenarios":"Invalid or revoked CLIENT_ID constant; wrong GITHUB_DOMAIN; the device_code has expired (422 'expired_token') or is invalid; rate limited (429); GitHub API outage (5xx); malformed request body (422 unsupported_grant_type).","commonSituations":"CLIENT_ID base64 constant drifted after GitHub rotated it; user waited past the device-code lifetime then the polling hit an expired device_code; corporate proxy returning a captive-portal HTML page (status 200 but non-JSON, or a 4xx).","solutions":["Parse the leading status code from the message — 404/422 on /login/device/code usually means a bad CLIENT_ID or GITHUB_DOMAIN.","For 'expired_token' from the polling endpoint, restart with startDeviceFlow() to get a fresh device_code.","For 429, respect Retry-After and back off; for 5xx, retry with jitter.","Confirm the CLIENT_ID decode still yields the expected GitHub OAuth App id."],"exampleFix":"// before\nconst data = (await fetchJson(urls.deviceCodeUrl, init)) as {...}\n\n// after\nlet data\ntry {\n  data = (await fetchJson(urls.deviceCodeUrl, init)) as {...}\n} catch (e) {\n  const msg = String(e)\n  if (/\\b404\\b|\\b422\\b/.test(msg)) throw new Error('GitHub rejected the client_id or domain. Check CLIENT_ID.')\n  throw e\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isHttpFailure(e: unknown): e is Error {\n  return e instanceof Error && /^\\d{3}\\s+/.test(e.message)\n}","tryCatchPattern":"try {\n  return await provider.startDeviceFlow()\n} catch (e) {\n  const msg = String(e)\n  if (/\\b429\\b/.test(msg)) {\n  await new Promise(r => setTimeout(r, backoffMs()))\n  return await provider.startDeviceFlow()\n  }\n  if (/\\b404\\b|\\b422\\b/.test(msg)) throw new Error('GitHub rejected CLIENT_ID or domain — check config.')\n  throw e\n}","preventionTips":["Verify the base64 CLIENT_ID decodes to the expected GitHub OAuth App id after every release.","Keep GITHUB_DOMAIN as 'github.com' unless GitHub documents otherwise.","Differentiate HTTP failures (this error) from polling application errors (raw.error) when surfacing to users."],"tags":["oauth","network","github","device-flow","http"],"backgroundTag":null,"analyzedSha":"81571269addb6bafb589a920b2883f1e1e084fd1","analyzedAt":"2026-08-12T21:51:44.981Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}