JanDeDobbeleer/oh-my-posh · error

authentication error: %s - %s

Error message

authentication error: %s - %s

What it means

During GitHub Copilot device-flow authentication, pollForToken polls GitHub's token endpoint until the user approves. When GitHub returns an OAuth error code that is not one of the expected transient states (authorization_pending, slow_down, expired_token, access_denied), the loop aborts and wraps the raw error code and description: "authentication error: %s - %s". It signals the device flow failed server-side for a reason the client did not specifically anticipate.

Source

Thrown at src/cli/auth/tui/copilot.go:161

		}

		if result.AccessToken != "" {
			return result.AccessToken, nil
		}

		switch result.Error {
		case "authorization_pending":
			continue
		case "slow_down":
			interval += 5
			continue
		case "expired_token":
			return "", fmt.Errorf("device code expired, please try again")
		case "access_denied":
			return "", fmt.Errorf("access was denied by the user")
		default:
			if result.Error != "" {
				return "", fmt.Errorf("authentication error: %s - %s", result.Error, result.ErrorDescription)
			}
		}
	}
}

func (c *CopilotAuth) status(err error) string {
	if err == nil {
		return "Successfully authenticated with GitHub Copilot"
	}

	httpErr, ok := err.(*http.Error)
	if !ok {
		return err.Error()
	}

	return fmt.Sprintf("HTTP error %d: %s", httpErr.StatusCode, httpErr.Error())
}

View on GitHub (pinned to 0976794618)

Solutions

  1. Read the error code and description echoed in the message and act on it (e.g. invalid_client -> update oh-my-posh so the bundled client_id is current)
  2. Re-run the authentication from scratch to obtain a fresh device code
  3. Check for network intermediaries (corporate proxy, SSL inspection) altering GitHub OAuth responses
  4. Update oh-my-posh to the latest version and retry; if it persists, report the exact code/description upstream

Example fix

// before
return "", fmt.Errorf("authentication error: %s - %s", result.Error, result.ErrorDescription)

// after (user side: retry the flow with a fresh device code)
// oh-my-posh auth ...   # start over; the old device code is unusable
Defensive patterns

Strategy: try-catch

Type guard

// Go: inspect the OAuth error code echoed in the message
func oauthErrorCode(err error) string {
	var parts []string
	if err != nil && strings.HasPrefix(err.Error(), "authentication error: ") {
		parts = strings.SplitN(strings.TrimPrefix(err.Error(), "authentication error: "), " - ", 2)
	}
	if len(parts) > 0 {
		return parts[0]
	}
	return ""
}

Try / catch

token, err := copilotAuth.Authenticate()
if err != nil {
	if strings.Contains(err.Error(), "authentication error:") {
		// surface the raw code/description, do not retry blindly
		log.Printf("device flow rejected: %v", err)
		return err
	}
	return err
}

Prevention

When it happens

Trigger: Calling Authenticate (e.g. `oh-my-posh auth login copilot`-style flow) and GitHub's /login/oauth/access_token responds with an unrecognized error field such as "unsupported_grant_type", "invalid_client", "invalid_grant", or "unauthorized_client" instead of an access token.

Common situations: GitHub changing or rejecting the client_id/device-code grant (outdated oh-my-posh build), enterprise/proxy endpoints responding with policy errors, malformed device_code reuse after expiry endpoints differ, or a corporate MITM proxy returning a non-standard OAuth error payload.

Understand the failure class

Related errors


AI-assisted analysis of JanDeDobbeleer/oh-my-posh@0976794618 (2026-08-31). Data as JSON: /api/errors/20b6327d206b5f13. Report an issue: GitHub.