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
- 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)
- Re-run the authentication from scratch to obtain a fresh device code
- Check for network intermediaries (corporate proxy, SSL inspection) altering GitHub OAuth responses
- 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
- Always start a fresh device-flow session instead of reusing an old device code
- Keep oh-my-posh updated so the bundled GitHub client_id stays valid
- Test authentication from a network without TLS-intercepting proxies first
- Log the full error code and description before retrying
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
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- received empty token
- no assets found
- failed to parse nerd fonts release
- the wasm build renders from data only and cannot make reques
- not connected
AI-assisted analysis of JanDeDobbeleer/oh-my-posh@0976794618 (2026-08-31).
Data as JSON: /api/errors/20b6327d206b5f13.
Report an issue: GitHub.