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
- Read the embedded error and error_description — e.g. 'invalid_client - client not allowed' pinpoints the cause
- For server_error or transient codes, retry the login after a short wait
- For invalid_client, update CLIProxyAPI to the latest release (client ID fix lands upstream)
- 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
- Branch on the embedded error code instead of string-matching the whole message
- invalid_client means upgrade; server_error means retry
- Report unhandled codes upstream
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
- kimi: failed to create device code request: %w
- kimi: device code request failed: %w
- kimi: failed to read device code response: %w
- kimi: device code request failed with status %d: %s
- kimi: failed to parse device code response: %w
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/769e0d01138c34c9.
Report an issue: GitHub.