{"record":{"id":"15d1c4c48369439e","repo":"mastra-ai/mastra","slug":"login-cancelled-15d1c4","errorCode":null,"errorMessage":"Login cancelled","messagePattern":"Login cancelled","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"info","filePath":"mastracode/sdk/src/auth/providers/github-copilot.ts","lineNumber":391,"sourceCode":" * Login with GitHub Copilot OAuth (device-code flow).\n *\n * Prompts for an optional GitHub Enterprise URL/domain, performs the device-code flow,\n * then exchanges the GitHub OAuth token for a Copilot bearer token.\n */\nexport async function loginGitHubCopilot(options: {\n  onAuth: (url: string, instructions?: string) => void;\n  onPrompt: (prompt: { message: string; placeholder?: string; allowEmpty?: boolean }) => Promise<string>;\n  onProgress?: (message: string) => void;\n  signal?: AbortSignal;\n}): Promise<GitHubCopilotCredentials> {\n  const input = await options.onPrompt({\n    message: 'GitHub Enterprise URL/domain (blank for github.com)',\n    placeholder: 'company.ghe.com',\n    allowEmpty: true,\n  });\n\n  if (options.signal?.aborted) {\n    throw new Error('Login cancelled');\n  }\n\n  const trimmed = input.trim();\n  const enterpriseDomain = normalizeDomain(input);\n  if (trimmed && !enterpriseDomain) {\n    throw new Error('Invalid GitHub Enterprise URL/domain');\n  }\n  let pending = await startGitHubCopilotDeviceLogin(enterpriseDomain ?? undefined, { signal: options.signal });\n  options.onAuth(pending.url, pending.instructions);\n\n  while (true) {\n    if (options.signal?.aborted) {\n      throw new Error('Login cancelled');\n    }\n\n    // Wait before polling (safety margin over the server interval), clamped to the deadline.\n    const remainingMs = Math.max(pending.deadlineAt - Date.now(), 0);\n    await abortableSleep(Math.min(copilotNextPollDelayMs(pending), remainingMs), options.signal);","sourceCodeStart":373,"sourceCodeEnd":409,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/mastracode/sdk/src/auth/providers/github-copilot.ts#L373-L409","documentation":"loginGitHubCopilot prompts interactively for an optional GitHub Enterprise URL/domain. If the user aborts the operation (via options.signal) after/while the enterprise-domain prompt is shown, the function throws this sentinel Error to signal a deliberate, user-initiated cancellation rather than a failure. Callers (loginPromise/login) treat it as an expected cancellation path.","triggerScenarios":"Calling login() for the GitHub Copilot provider and aborting the supplied AbortSignal (or pressing Ctrl-C/Escape in the interactive prompt flow) while the 'GitHub Enterprise URL/domain' prompt is pending or immediately after it returns, before device login starts.","commonSituations":"Users cancel the CLI login flow; a timeout or wrapper script aborts the signal while waiting for stdin input; an embedding app cancels a login promise that is blocked on the interactive prompt.","solutions":["Re-run login() and complete (or leave blank) the enterprise-domain prompt without aborting","If cancellation was accidental, check any wrappers/scripts that abort the signal (timeouts, Ctrl-C forwarding) during the prompt","Handle this error distinctly in your catch block and treat it as a no-op cancellation, not a failure to report"],"exampleFix":"// before\nawait login('github-copilot').catch(err => { throw err; });\n// after\ntry {\n  await login('github-copilot');\n} catch (err) {\n  if (err instanceof Error && err.message === 'Login cancelled') return; // expected user cancel\n  throw err;\n}","handlingStrategy":"try-catch","validationCode":"// Abort only intentionally; check before starting login\nif (options.signal?.aborted) {\n  return; // skip login entirely instead of entering the prompt flow\n}","typeGuard":"function isLoginCancelled(err: unknown): boolean {\n  return err instanceof Error && err.message === 'Login cancelled';\n}","tryCatchPattern":"try {\n  await login('github-copilot', { signal });\n} catch (err) {\n  if (isLoginCancelled(err)) return; // user-initiated, not a failure\n  throw err;\n}","preventionTips":["Wire the AbortSignal deliberately — only abort when the user actually cancels","Distinguish 'Login cancelled' from real auth errors in your error handling","Avoid attaching aggressive timeouts to the signal used for interactive login prompts"],"tags":["auth","abort","user-cancellation","github-copilot"],"backgroundTag":"auth-flow-cancelled","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T08:17:16.595Z"}