github/github-mcp-server · error

awaiting device authorization: %w

Error message

awaiting device authorization: %w

What it means

The device-flow polling loop (oc.DeviceAccessToken) failed to obtain a token: the user denied the code, the code or device session expired (GitHub device codes live ~15 minutes), polling hit an unrecoverable error, or the waiting context was canceled. This runs inside flowPlan.run, so it fails the background flow after the user has been shown the code. The oauth2 library maps the RFC 8628 error responses (access_denied, expired_token, slow_down) and only the terminal ones surface here.

Source

Thrown at internal/oauth/flow.go:172

}

// beginDevice prepares the device authorization flow. It requests a device code
// up front (so the code can be displayed) and selects a display channel:
// URL elicitation, then form elicitation, then a tool-response message.
func (m *Manager) beginDevice(prompter Prompter) (*flowPlan, error) {
	oc := m.oauth2Config("")

	ctx, cancel := context.WithTimeout(context.Background(), deviceAuthTimeout)
	defer cancel()
	da, err := oc.DeviceAuth(ctx)
	if err != nil {
		return nil, fmt.Errorf("requesting device code: %w", err)
	}

	run := func(ctx context.Context) (*oauth2.Token, error) {
		tok, err := oc.DeviceAccessToken(ctx, da)
		if err != nil {
			return nil, fmt.Errorf("awaiting device authorization: %w", err)
		}
		return tok, nil
	}

	// As with PKCE, the manual instructions double as the runtime fallback, so
	// build them once and reuse for both display plans and the last resort.
	manual := &UserAction{
		URL:      da.VerificationURI,
		UserCode: da.UserCode,
		Message: fmt.Sprintf(
			"%s\n\nAfter authorizing, retry your request.\n\n%s",
			deviceInstruction(da), securityAdvisory,
		),
	}

	if canPromptURL(prompter) {
		display := func(ctx context.Context) error {
			return prompter.PromptURL(ctx, Prompt{

View on GitHub (pinned to 0ea1f775a7)

Solutions

  1. Retry the login and act on the code promptly — expiry and denial dominate
  2. If the user denied, re-consent on the next attempt or use an account/org where the app is allowed
  3. Keep the client session alive until the flow completes; cancellation aborts the poll
  4. For persistent failures, read the wrapped oauth2 error body for the endpoint's exact message
Defensive patterns

Strategy: retry

Try / catch

if err != nil && strings.Contains(err.Error(), "awaiting device authorization") {
    if strings.Contains(err.Error(), "denied") { /* user refusal: prompt again or stop */ }
    if strings.Contains(err.Error(), "expired") { /* restart flow, act on the code faster */ }
    if errors.Is(ctx.Err(), context.Canceled) { /* client gave up; nothing to fix */ }
}

Prevention

When it happens

Trigger: oc.DeviceAccessToken at internal/oauth/flow.go:170 returns an error when: the user clicks 'Deny' at github.com/login/device (access_denied); the user_code expires before authorization completes (expired_token — typical when the code was displayed but nobody acted for ~15 min); the parent context is canceled (client disconnected, server shutting down); the token endpoint becomes unreachable mid-poll.

Common situations: User never visits the verification URI because the prompt got lost in MCP client output; user denies the 'github-mcp-server' authorization; long-running unattended login attempt where the code expired; client cancellation when the user gives up and closes the session; network drop during the polling window.

Related errors


AI-assisted analysis of github/github-mcp-server@0ea1f775a7 (2026-08-15). Data as JSON: /api/errors/856300f4da14a2f9. Report an issue: GitHub.