{"record":{"id":"1b51c977a567796f","repo":"mastra-ai/mastra","slug":"failed-to-refresh-the-github-copilot-token","errorCode":null,"errorMessage":"Failed to refresh the GitHub Copilot token.","messagePattern":"Failed to refresh the GitHub Copilot token\\.","errorType":"exception","errorClass":"ProviderAuthRequiredError","httpStatus":null,"severity":"error","filePath":"mastracode/sdk/src/providers/github-copilot.ts","lineNumber":133,"sourceCode":" * - Adds the VS Code-like Copilot headers required by the API.\n * - Rewrites the request URL onto the per-token API base when `rewriteUrl` is true.\n */\nexport function buildGitHubCopilotOAuthFetch(\n  opts: { authStorage?: CredentialStore; rewriteUrl?: boolean } = {},\n): typeof fetch {\n  return (async (url: string | URL | Request, init?: Parameters<typeof fetch>[1]) => {\n    const storage = opts.authStorage ?? getAuthStorage();\n    storage.reload();\n\n    const cred = storage.get(COPILOT_PROVIDER_ID);\n    if (!cred || cred.type !== 'oauth') {\n      throw new ProviderAuthRequiredError('Not logged in to GitHub Copilot.');\n    }\n\n    // getApiKey() refreshes the Copilot bearer if it has expired.\n    const accessToken = await storage.getApiKey(COPILOT_PROVIDER_ID);\n    if (!accessToken) {\n      throw new ProviderAuthRequiredError('Failed to refresh the GitHub Copilot token.');\n    }\n    storage.reload();\n\n    const enterpriseUrl = (cred as GitHubCopilotCredentials).enterpriseUrl;\n\n    let parsedBody: unknown;\n    if (typeof init?.body === 'string') {\n      try {\n        parsedBody = JSON.parse(init.body);\n      } catch {\n        parsedBody = undefined;\n      }\n    }\n    const isAgent = detectIsAgent(parsedBody);\n    const isVision = detectIsVision(parsedBody);\n\n    // Preserve non-auth headers from caller.\n    const headers = new Headers();","sourceCodeStart":115,"sourceCodeEnd":151,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/mastracode/sdk/src/providers/github-copilot.ts#L115-L151","documentation":"After confirming an OAuth credential exists, the Copilot wrapper calls storage.getApiKey(), which transparently refreshes an expired Copilot bearer token. If the refresh fails and no access token can be produced, ProviderAuthRequiredError('Failed to refresh the GitHub Copilot token.') is thrown. Unlike error 768, the user did log in, but their token could not be renewed.","triggerScenarios":"Using `copilot` or `fetchWithOAuth` when the stored Copilot bearer token has expired and `storage.getApiKey(COPILOT_PROVIDER_ID)` cannot obtain a new one (refresh endpoint fails, refresh token revoked, GitHub session invalidated).","commonSituations":"Long-lived CI cache holding an expired token; GitHub password change or session revocation invalidating refresh tokens; network failure or GitHub outage during the refresh exchange; GitHub Copilot access removed from the account, making refresh return 4xx.","solutions":["Re-run the GitHub Copilot login/device flow to obtain fresh tokens","Check network reachability to GitHub's token endpoints and retry","Inspect auth storage for a corrupt/expired refresh token and delete it so a clean login is forced","Verify the GitHub account still has Copilot access and no revoked sessions (github.com/settings/security)"],"exampleFix":"// before\nconst provider = copilot(); // throws on expired, unrefreshable token\n// after\ntry {\n  return copilot();\n} catch (e) {\n  if (e instanceof ProviderAuthRequiredError) {\n    storage.remove?.('github-copilot');\n    await copilotDeviceFlowLogin(); // fresh tokens\n    return copilot();\n  }\n  throw e;\n}","handlingStrategy":"try-catch","validationCode":"// Refreshability is only observable by calling getApiKey; pre-check the stored credential age\nconst cred = storage.get('github-copilot');\nif (cred?.type === 'oauth' && 'expiresAt' in cred && Date.now() > (cred as { expiresAt: number }).expiresAt) {\n  console.warn('Copilot token expired; a refresh will be attempted on next use.');\n}","typeGuard":"function isFreshToken(c: { type: string; expiresAt?: number } | null): boolean {\n  return !!c && c.type === 'oauth' && (c.expiresAt == null || c.expiresAt > Date.now());\n}","tryCatchPattern":"try {\n  await runWithCopilot();\n} catch (err) {\n  if (err instanceof ProviderAuthRequiredError && err.message.includes('refresh')) {\n    await copilotDeviceFlowLogin(); // re-authenticate to get fresh tokens, then retry\n  } else throw err;\n}","preventionTips":["Re-run login periodically or on any refresh failure; refresh tokens can be revoked","Ensure network access to GitHub token endpoints in CI (common silent refresh killer)","Delete stale credential entries so a forced clean login replaces bad refresh tokens","Verify Copilot access is still active on the GitHub account"],"tags":["auth","oauth","token-refresh"],"backgroundTag":"oauth-token-refresh-failed","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}