{"record":{"id":"8e9ca4ce5426bf0b","repo":"decolua/9router","slug":"github-authentication-failed-error-message","errorCode":null,"errorMessage":"GitHub authentication failed: ${error.message}","messagePattern":"GitHub authentication failed: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/lib/oauth/services/github.js","lineNumber":181,"sourceCode":"      const userInfo = await this.getUserInfo(tokenResponse.access_token);\n      \n      console.log(`\\n✅ Successfully authenticated as ${userInfo.login}`);\n      \n      return {\n        accessToken: tokenResponse.access_token,\n        copilotToken: copilotToken.token,\n        refreshToken: null, // GitHub device flow doesn't return refresh token\n        expiresIn: copilotToken.expires_at,\n        userInfo: {\n          id: userInfo.id,\n          login: userInfo.login,\n          name: userInfo.name,\n          email: userInfo.email,\n        },\n        copilotTokenInfo: copilotToken,\n      };\n    } catch (error) {\n      throw new Error(`GitHub authentication failed: ${error.message}`);\n    }\n  }\n\n  /**\n   * Connect to server with GitHub credentials\n   */\n  async connect() {\n    try {\n      // Authenticate with GitHub\n      const authResult = await this.authenticate();\n      \n      // Send credentials to server\n      const { server, token, userId } = await import(\"../config/index.js\").then(m => m.getServerCredentials());\n      const spinner = (await import(\"../utils/ui.js\")).spinner(\"Connecting to server...\").start();\n      \n      const response = await fetch(`${server}/api/cli/providers/github`, {\n        method: \"POST\",\n        headers: {","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/src/lib/oauth/services/github.js#L163-L199","documentation":"GitHubService.authenticate() orchestrates the whole device flow: getDeviceCode → pollAccessToken → getCopilotToken → getUserInfo. Any error thrown by those steps is caught and re-wrapped as `GitHub authentication failed: ${error.message}` at github.js:181. This is a wrapper message — the root cause is the inner error text (e.g. 'Device code expired', 'Access denied', or an HTTP failure).","triggerScenarios":"Any failure inside authenticate(): device-code request failed (263), user never authorized in time (264), user denied (265), unrecognized poll error (266), Copilot token rejected (267), or user-info fetch failed (268).","commonSituations":"All the underlying situations above; additionally, because the wrapper discards the original stack, developers often see only this generic line when debugging CI or scripted runs where the inner spinner output was suppressed.","solutions":["Read the text after 'GitHub authentication failed:' — it contains the inner error; map it to the specific step and fix listed for errors 263–268.","If the inner message is empty, add temporary logging of the caught error (message + stack) to identify the failing step.","Re-run authenticate() from a network that can reach github.com and complete the browser authorization promptly.","Consider preserving the original error (`throw new Error(..., { cause: error })`) for better stack traces."],"exampleFix":"// before\n} catch (error) {\n  throw new Error(`GitHub authentication failed: ${error.message}`);\n}\n// after\n} catch (error) {\n  throw new Error(`GitHub authentication failed: ${error.message}`, { cause: error });\n}","handlingStrategy":"try-catch","validationCode":"async function canReachGitHub() {\n  const res = await fetch('https://github.com', { method: 'HEAD' }).catch(() => null);\n  if (!res) throw new Error('GitHub unreachable — device authentication will fail');\n}","typeGuard":"function isAuthResult(r) {\n  return r !== null && typeof r === 'object' && typeof r.accessToken === 'string' && typeof r.copilotToken === 'string' && r.userInfo?.login;\n}","tryCatchPattern":"try {\n  const auth = await service.authenticate();\n} catch (err) {\n  const inner = err.cause ?? err;\n  console.error(`GitHub auth failed at: ${inner.message}`); // unwrap to see the real failing step\n  if (inner.message === 'Device code expired') return retryWithFreshCode();\n  if (inner.message === 'Access denied') return promptUserToRetry();\n  throw err;\n}","preventionTips":["Unwrap the wrapper message to identify the failing step (device code, poll, Copilot token, or user info) before fixing.","Pre-flight network reachability to github.com before scripted/CI authentication runs.","Prefer `{ cause: error }` rethrows in your own wrappers so stacks aren't lost.","Complete browser authorization promptly — the majority of authenticate() failures are user-timing related."],"tags":["oauth","github","device-flow","authentication"],"backgroundTag":"oauth-flow-failed","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}