{"record":{"id":"471aa61fc9d397ca","repo":"mastra-ai/mastra","slug":"invalid-copilot-token-response","errorCode":null,"errorMessage":"Invalid Copilot token response","messagePattern":"Invalid Copilot token response","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"mastracode/sdk/src/auth/providers/github-copilot.ts","lineNumber":349,"sourceCode":"  signal?: AbortSignal,\n): Promise<GitHubCopilotCredentials> {\n  const domain = enterpriseDomain || 'github.com';\n  const urls = getUrls(domain);\n\n  const raw = await fetchJson(\n    urls.copilotTokenUrl,\n    {\n      headers: {\n        Accept: 'application/json',\n        Authorization: `Bearer ${refreshToken}`,\n        ...COPILOT_HEADERS,\n      },\n    },\n    signal,\n  );\n\n  if (!raw || typeof raw !== 'object') {\n    throw new Error('Invalid Copilot token response');\n  }\n\n  const obj = raw as Record<string, unknown>;\n  const token = obj.token;\n  const expiresAt = obj.expires_at;\n\n  if (typeof token !== 'string' || typeof expiresAt !== 'number') {\n    throw new Error('Invalid Copilot token response fields');\n  }\n\n  const credentials: GitHubCopilotCredentials = {\n    refresh: refreshToken,\n    access: token,\n    // expires_at is seconds; subtract 5 minutes so we refresh before actual expiry.\n    expires: expiresAt * 1000 - 5 * 60 * 1000,\n  };\n  if (enterpriseDomain) {\n    credentials.enterpriseUrl = enterpriseDomain;","sourceCodeStart":331,"sourceCodeEnd":367,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/mastracode/sdk/src/auth/providers/github-copilot.ts#L331-L367","documentation":"`refreshGitHubCopilotToken` exchanges the stored GitHub OAuth token for a Copilot bearer token at `https://api.<domain>/copilot_internal/v2/token`. If the response is ok but not a JSON object (or empty), the SDK throws this error before extracting `token`/`expires_at`. It signals the Copilot internal token endpoint returned an unexpected payload.","triggerScenarios":"The Copilot token endpoint returned 200 with an empty/array/string body, or a gateway/proxy responded with ok but non-token JSON. Common with a wrong enterprise domain producing an unexpected api host (`copilot-api.<enterpriseDomain>`), or a proxy intercepting `api.individual.githubcopilot.com`.","commonSituations":"Expired/revoked GitHub OAuth token causing a non-standard body, GHE instances where `copilot_internal` endpoints behave differently, misconfigured enterprise domain, corporate proxies/CDNs replacing the response.","solutions":["Check network interception: log the raw body of the `/copilot_internal/v2/token` response","Verify the Copilot API base URL resolution — with no `proxy-ep` in the token it falls back to `https://api.individual.githubcopilot.com` or `https://copilot-api.<enterpriseDomain>`","If 401-like bodies arrive with ok status, redo the device-flow login to get a fresh GitHub token","Bypass corporate proxies or add them to the allowlist for githubcopilot.com hosts","Update the SDK if GitHub changed the internal token endpoint response envelope"],"exampleFix":"// before: refresh against an unverifiable host\nconst creds = await refreshGitHubCopilotToken(githubToken); // throws 'Invalid Copilot token response'\n// after: verify the endpoint returns the expected envelope first\nconst res = await fetch('https://api.individual.githubcopilot.com/copilot_internal/v2/token', { headers: { Authorization: `token ${githubToken}` } });\nconst body = await res.json();\nconsole.log(res.status, typeof body, Object.keys(body)); // expect { token: string, expires_at: number }","handlingStrategy":"type-guard","validationCode":"// Verify your GitHub token and the endpoint before refreshing\nfunction copilotApiHost(enterpriseDomain?: string): string {\n  return enterpriseDomain ? `https://copilot-api.${enterpriseDomain}` : 'https://api.individual.githubcopilot.com';\n}\nif (!githubToken || githubToken.length < 20) throw new Error('GitHub OAuth token missing/invalid — redo device flow before refreshing Copilot token');","typeGuard":"function isCopilotTokenResponse(v: unknown): v is { token: string; expires_at: number } {\n  return !!v && typeof v === 'object' && typeof (v as Record<string, unknown>).token === 'string' && typeof (v as Record<string, unknown>).expires_at === 'number';\n}","tryCatchPattern":"try {\n  const creds = await provider.refreshToken();\n} catch (e) {\n  if (e instanceof Error && e.message === 'Invalid Copilot token response') {\n    console.error('Copilot token endpoint returned a non-object body — check enterprise domain config and proxies', e.message);\n    return startFreshDeviceFlow(); // stale/revoked GitHub token often the cause\n  }\n  throw e;\n}","preventionTips":["Refresh Copilot tokens proactively before expires_at minus 5 minutes instead of letting them lapse","Redo device-flow login on any unexpected token-endpoint payload; GitHub tokens expire/revocable","Verify enterprise domain so `copilot-api.<domain>` / api hosts resolve correctly","Allowlist githubcopilot.com hosts in proxies and inspect raw bodies when failures occur"],"tags":["github-copilot","token-refresh","response-shape","network"],"backgroundTag":"unexpected-api-response-shape","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}