router-for-me/CLIProxyAPI · error
kimi: failed to parse device code response: %w
Error message
kimi: failed to parse device code response: %w
What it means
A 200 response from the device authorization endpoint contained a body that is not valid JSON matching DeviceCodeResponse. The client expects fields like device_code, user_code, verification_uri, interval, expires_in. An HTML error page, empty body, or changed schema will fail json.Unmarshal.
Source
Thrown at internal/auth/kimi/kimi.go:216
}
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 {
interval = defaultPollInterval
}
deadline := time.Now().Add(maxPollDuration)
if deviceCode.ExpiresIn > 0 {View on GitHub (pinned to 78f0c4079e)
Solutions
- Log or print the raw body bytes before unmarshal to see what the server actually returned (HTML vs JSON vs empty)
- Retry once — some WAF challenges are one-time; a fresh request may get the real JSON
- Disable interfering proxies for auth.kimi.com or add it to a proxy bypass list
- If the JSON shape genuinely changed, update DeviceCodeResponse in internal/auth/kimi/kimi.go or pull the latest CLIProxyAPI release
Defensive patterns
Strategy: validation
Validate before calling
// Pre-check the raw body before unmarshal in forks/tests
if len(bodyBytes) == 0 || bodyBytes[0] != '{' {
return nil, fmt.Errorf("kimi: device code response is not JSON: %.120s", bodyBytes)
} Try / catch
if err != nil {
var syntaxErr *json.SyntaxError
if errors.As(err, &syntaxErr) {
// body was not JSON: check proxies / WAF, retry once
}
return err
} Prevention
- Check Content-Type of responses in debug logs before assuming API JSON
- Bypass proxies for auth.kimi.com
- Log raw bodies on parse failures to diagnose HTML challenges fast
When it happens
Trigger: auth.kimi.com returns 200 with an HTML interstitial (Cloudflare challenge, consent page), a WAF-modified response, a body truncated by the proxy so JSON is incomplete, or Moonshot renames response fields so the struct no longer matches.
Common situations: Cloudflare/WAF injection on the auth host, transparent proxies rewriting responses, API schema changes after a Kimi update (fix upstream), locale-dependent error pages returned with status 200.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- kimi: failed to parse token response: %w
- kimi: failed to parse refresh response: %w
- kimi: failed to create device code request: %w
- kimi: device code request failed: %w
- kimi: failed to read device code response: %w
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/d758ba87936e97d7.
Report an issue: GitHub.