router-for-me/CLIProxyAPI · error

kimi: OAuth error: %s - %s

Error message

kimi: OAuth error: %s - %s

What it means

The token endpoint returned an OAuth error code not in the handled set (authorization_pending, slow_down, expired_token, access_denied). Both the error code and error_description are embedded. This is the catch-all for server-side OAuth failures such as invalid_grant, invalid_client, invalid_request, or unsupported_grant_type.

Source

Thrown at internal/auth/kimi/kimi.go:324

		Scope            string  `json:"scope"`
	}

	if err = json.Unmarshal(bodyBytes, &oauthResp); err != nil {
		return nil, fmt.Errorf("kimi: failed to parse token response: %w", err), false
	}

	if oauthResp.Error != "" {
		switch oauthResp.Error {
		case "authorization_pending":
			return nil, nil, true // Continue polling
		case "slow_down":
			return nil, nil, true // Continue polling (with increased interval handled by caller)
		case "expired_token":
			return nil, fmt.Errorf("kimi: device code expired"), false
		case "access_denied":
			return nil, fmt.Errorf("kimi: access denied by user"), false
		default:
			return nil, fmt.Errorf("kimi: OAuth error: %s - %s", oauthResp.Error, oauthResp.ErrorDescription), false
		}
	}

	if oauthResp.AccessToken == "" {
		return nil, fmt.Errorf("kimi: empty access token in response"), false
	}

	var expiresAt int64
	if oauthResp.ExpiresIn > 0 {
		expiresAt = time.Now().Unix() + int64(oauthResp.ExpiresIn)
	}

	return &KimiTokenData{
		AccessToken:  oauthResp.AccessToken,
		RefreshToken: oauthResp.RefreshToken,
		TokenType:    oauthResp.TokenType,
		ExpiresAt:    expiresAt,
		Scope:        oauthResp.Scope,

View on GitHub (pinned to 78f0c4079e)

Solutions

  1. Read the embedded error and error_description — e.g. 'invalid_client - client not allowed' pinpoints the cause
  2. For server_error or transient codes, retry the login after a short wait
  3. For invalid_client, update CLIProxyAPI to the latest release (client ID fix lands upstream)
  4. For invalid_grant, clear stale state under auths/ and start a fresh login
Defensive patterns

Strategy: try-catch

Type guard

func extractKimiOAuthError(err error) (code, desc string, ok bool) {
    m := regexp.MustCompile(`kimi: OAuth error: (\S+) - (.*)`).FindStringSubmatch(err.Error())
    if len(m) == 3 { return m[1], m[2], true }
    return "", "", false
}

Try / catch

if code, desc, ok := extractKimiOAuthError(err); ok {
    switch code {
    case "server_error":
        time.Sleep(5 * time.Second); return retry()
    case "invalid_client":
        return fmt.Errorf("client ID invalidated; update CLIProxyAPI")
    }
}

Prevention

When it happens

Trigger: invalid_client if the hardcoded kimiClientID is invalidated by Moonshot; invalid_grant for a malformed/replayed device_code; server introduces new error codes; temporary server misconfiguration returning server_error.

Common situations: Client ID rotation on Kimi's side (requires updating this repo's kimiClientID constant), corrupted auths/ storage feeding a bad device code, new OAuth error variants after upstream changes.

Related errors


AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15). Data as JSON: /api/errors/769e0d01138c34c9. Report an issue: GitHub.