{"record":{"id":"5ea8b9b48d325bc4","repo":"mastra-ai/mastra","slug":"response-status-response-statustext-text","errorCode":null,"errorMessage":"${response.status} ${response.statusText}: ${text}","messagePattern":"\\$\\{response\\.status\\} \\$\\{response\\.statusText\\}: \\$\\{text\\}","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"mastracode/sdk/src/auth/providers/github-copilot.ts","lineNumber":114,"sourceCode":"\n/**\n * Resolve the Copilot API base URL.\n * Prefers the `proxy-ep` parsed from the bearer token, then falls back to enterprise/individual defaults.\n */\nexport function getGitHubCopilotBaseUrl(token?: string, enterpriseDomain?: string): string {\n  if (token) {\n    const fromToken = getBaseUrlFromToken(token);\n    if (fromToken) return fromToken;\n  }\n  if (enterpriseDomain) return `https://copilot-api.${enterpriseDomain}`;\n  return 'https://api.individual.githubcopilot.com';\n}\n\nasync function fetchJson(url: string, init: RequestInit, signal?: AbortSignal): Promise<unknown> {\n  const response = await fetch(url, signal ? { ...init, signal } : init);\n  if (!response.ok) {\n    const text = await response.text().catch(() => '');\n    throw new Error(`${response.status} ${response.statusText}: ${text}`);\n  }\n  return response.json();\n}\n\nasync function startDeviceFlow(domain: string, signal?: AbortSignal): Promise<DeviceCodeResponse> {\n  const urls = getUrls(domain);\n  const data = await fetchJson(\n    urls.deviceCodeUrl,\n    {\n      method: 'POST',\n      headers: {\n        Accept: 'application/json',\n        'Content-Type': 'application/x-www-form-urlencoded',\n        'User-Agent': COPILOT_USER_AGENT,\n      },\n      body: new URLSearchParams({\n        client_id: CLIENT_ID,\n        scope: 'read:user',","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/mastracode/sdk/src/auth/providers/github-copilot.ts#L96-L132","documentation":"This is the generic HTTP failure thrown by the SDK's GitHub Copilot auth `fetchJson` helper whenever the GitHub OAuth/device-code or Copilot token endpoint returns a non-2xx response. The message embeds the HTTP status, status text, and the raw response body, so it directly reflects what the GitHub server rejected. It surfaces during device-flow login (`startDeviceFlow`, token polling) and Copilot bearer-token refresh.","triggerScenarios":"Any non-ok response from `POST https://github.com/login/device/code`, `POST .../login/oauth/access_token`, or `POST https://api.<domain>/copilot_internal/v2/token` — e.g. 401 after the user denied the device flow, 400 from a wrong `client_id` or malformed body, 404/redirect from an incorrect enterprise domain, 429 slow-down during polling, 5xx from GitHub.","commonSituations":"Polling `access_token` before the user finishes entering the device code (expected `authorization_pending` 400s surfaced as this error if not handled), misconfigured GHES/GHE domain so URLs point at the wrong host, expired or revoked GitHub OAuth grant during token refresh, corporate proxy or rate limiting returning 403/429.","solutions":["Read the status and body in the message: `authorization_pending`/`slow_down` bodies mean keep polling — ensure your caller tolerates these instead of crashing","Verify the configured GitHub domain (github.com vs your GHES hostname) — a wrong domain produces 404/SSL errors","Check network/proxy reachability to github.com and api.github.com and retry 5xx/429 with backoff","If 401/403 on the Copilot token endpoint, redo the full device-flow login to obtain a fresh GitHub OAuth token","Confirm the SDK's client_id/endpoint config matches the installed version if the API contract changed"],"exampleFix":"// before: treating every non-ok response as fatal, even during polling\nconst creds = await provider.device(); // throws on authorization_pending\n// after: catch and branch on the pending/slow-down bodies\ntry {\n  const creds = await provider.device();\n} catch (e) {\n  const m = String((e as Error).message);\n  if (m.includes('authorization_pending') || m.includes('slow_down')) {\n    await sleep(intervalMs);\n    return; // keep polling\n  }\n  throw e;\n}","handlingStrategy":"try-catch","validationCode":"// Precheck connectivity and domain before calling the provider\nasync function canReach(url: string): Promise<boolean> {\n  try { const r = await fetch(url, { method: 'HEAD' }); return r.status < 500 || r.status !== 404; } catch { return false; }\n}\nawait canReach('https://github.com/login/device/code');","typeGuard":"function isHttpErrorWithStatus(e: unknown): e is Error & { message: string } {\n  return e instanceof Error && /^\\d{3} /.test(e.message);\n}\nfunction parseStatus(msg: string): number | null {\n  const m = /^(\\d{3}) /.exec(msg);\n  return m ? Number(m[1]) : null;\n}","tryCatchPattern":"try {\n  const creds = await provider.device();\n} catch (e) {\n  const msg = e instanceof Error ? e.message : String(e);\n  const status = parseStatus(msg);\n  if (status === 429 || (status ?? 500) >= 500) { await backoff(); return retry(); }\n  if (msg.includes('authorization_pending') || msg.includes('slow_down')) { await sleep(intervalMs); return; }\n  if (status === 401 || status === 403) { return startFreshDeviceFlow(); }\n  throw e;\n}","preventionTips":["Handle authorization_pending/slow_down bodies as part of normal device-flow polling, not fatal errors","Validate the GitHub domain config (github.com vs GHES host) before login","Apply exponential backoff on 5xx/429 responses","Monitor for 401/403 on token refresh and trigger re-login proactively","Log the embedded response body from the error message for GitHub-side diagnostics"],"tags":["network","http","github-copilot","oauth","device-flow"],"backgroundTag":"http-non-2xx-response","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}