router-for-me/CLIProxyAPI · error

kimi: device code request failed with status %d: %s

Error message

kimi: device code request failed with status %d: %s

What it means

Kimi's device authorization endpoint returned a non-200 HTTP status. The full response body is embedded in the error message, so the server's explanation (rate limit, invalid client, server error) is visible. The request itself is well-formed; failure comes from the server side or the request's client_id.

Source

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

	}

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("kimi: device code request failed: %w", err)
	}
	defer func() {
		if errClose := resp.Body.Close(); errClose != nil {
			log.Errorf("kimi device code: close body error: %v", errClose)
		}
	}()

	bodyBytes, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("kimi: failed to read device code response: %w", err)
	}

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("kimi: device code request failed with status %d: %s", resp.StatusCode, string(bodyBytes))
	}

	var deviceCode DeviceCodeResponse
	if err = json.Unmarshal(bodyBytes, &deviceCode); err != nil {
		return nil, fmt.Errorf("kimi: failed to parse device code response: %w", err)
	}

	return &deviceCode, nil
}

// PollForToken polls the token endpoint until the user authorizes or the device code expires.
func (c *DeviceFlowClient) PollForToken(ctx context.Context, deviceCode *DeviceCodeResponse) (*KimiTokenData, error) {
	if deviceCode == nil {
		return nil, fmt.Errorf("kimi: device code is nil")
	}

	interval := time.Duration(deviceCode.Interval) * time.Second
	if interval < defaultPollInterval {

View on GitHub (pinned to 78f0c4079e)

Solutions

  1. Read the embedded body in the error message — it names the actual cause (e.g. 'rate_limited', 'invalid_client')
  2. If 429: wait the interval indicated (or 60s+) before requesting a new device code instead of retrying in a loop
  3. Update CLIProxyAPI to the latest release — a changed/rotated kimiClientID is fixed upstream in internal/auth/kimi/kimi.go
  4. If blocked (403) from a datacenter/VPN IP, run the login from a residential network or allowed egress IP
Defensive patterns

Strategy: try-catch

Type guard

func isDeviceCodeStatusErr(err error) bool {
    return err != nil && strings.Contains(err.Error(), "device code request failed with status")
}

Try / catch

if err != nil {
    if strings.Contains(err.Error(), "status 429") {
        // back off before next login attempt
        time.Sleep(time.Minute)
    }
    return err // body in message carries the server's reason
}

Prevention

When it happens

Trigger: POST to /api/oauth/device_authorization with the hardcoded client_id 17e5f671-... returns 4xx/5xx: 429 rate limiting from too many login attempts, 4xx if Moonshot invalidates or restricts the client ID, 5xx during auth.kimi.com outages, or Cloudflare-style 403 blocks for datacenter IPs.

Common situations: Version drift where Kimi changes or deprecates its OAuth client ID (fix requires updating this repo), repeated login retries triggering rate limits, running the proxy from a cloud IP that Kimi's WAF blocks, auth.kimi.com regional outage.

Related errors


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